home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir37 / ms_sh23s.zip / SRC / SH7.C < prev    next >
C/C++ Source or Header  |  1994-08-26  |  121KB  |  5,745 lines

  1. /*
  2.  * MS-DOS SHELL - Internal Command Processing
  3.  *
  4.  * MS-DOS SHELL - Copyright (c) 1990,4 Data Logic Limited and Charles Forsyth
  5.  *
  6.  * This code is based on (in part) the shell program written by Charles
  7.  * Forsyth and is subject to the following copyright restrictions.  The
  8.  * code for the test (dotest) command was based on code written by
  9.  * Erik Baalbergen.  The following copyright conditions apply:
  10.  *
  11.  * 1.  Redistribution and use in source and binary forms are permitted
  12.  *     provided that the above copyright notice is duplicated in the
  13.  *     source form and the copyright notice in file sh6.c is displayed
  14.  *     on entry to the program.
  15.  *
  16.  * 2.  The sources (or parts thereof) or objects generated from the sources
  17.  *     (or parts of sources) cannot be sold under any circumstances.
  18.  *
  19.  *    $Header: /usr/users/istewart/shell/sh2.3/Release/RCS/sh7.c,v 2.18 1994/08/25 20:49:11 istewart Exp $
  20.  *
  21.  *    $Log: sh7.c,v $
  22.  *    Revision 2.18  1994/08/25  20:49:11  istewart
  23.  *    MS Shell 2.3 Release
  24.  *
  25.  *    Revision 2.17  1994/02/23  09:23:38  istewart
  26.  *    Beta 233 updates
  27.  *
  28.  *    Revision 2.16  1994/02/01  10:25:20  istewart
  29.  *    Release 2.3 Beta 2, including first NT port
  30.  *
  31.  *    Revision 2.15  1994/01/20  14:51:43  istewart
  32.  *    Release 2.3 Beta 1
  33.  *
  34.  *    Revision 2.14  1994/01/11  17:55:25  istewart
  35.  *    Release 2.3 Beta 0 patches
  36.  *
  37.  *    Revision 2.13  1993/12/01  11:58:34  istewart
  38.  *    Release 226 beta
  39.  *
  40.  *    Revision 2.12  1993/11/09  10:39:49  istewart
  41.  *    Beta 226 checking
  42.  *
  43.  *    Revision 2.11  1993/08/25  16:03:57  istewart
  44.  *    Beta 225 - see Notes file
  45.  *
  46.  *    Revision 2.10  1993/07/02  10:21:35  istewart
  47.  *    224 Beta fixes
  48.  *
  49.  *    Revision 2.9  1993/06/14  11:00:12  istewart
  50.  *    More changes for 223 beta
  51.  *
  52.  *    Revision 2.8  1993/06/02  09:52:35  istewart
  53.  *    Beta 223 Updates - see Notes file
  54.  *
  55.  *    Revision 2.7  1993/02/16  16:03:15  istewart
  56.  *    Beta 2.22 Release
  57.  *
  58.  *    Revision 2.6  1993/01/26  18:35:09  istewart
  59.  *    Release 2.2 beta 0
  60.  *
  61.  *    Revision 2.5  1992/12/14  10:54:56  istewart
  62.  *    BETA 215 Fixes and 2.1 Release
  63.  *
  64.  *    Revision 2.4  1992/11/06  10:03:44  istewart
  65.  *    214 Beta test updates
  66.  *
  67.  *    Revision 2.3  1992/09/03  18:54:45  istewart
  68.  *    Beta 213 Updates
  69.  *
  70.  *    Revision 2.2  1992/07/16  14:33:34  istewart
  71.  *    Beta 212 Baseline
  72.  *
  73.  *    Revision 2.1  1992/07/10  10:52:48  istewart
  74.  *    211 Beta updates
  75.  *
  76.  *    Revision 2.0  1992/05/07  21:33:35  Ian_Stewartson
  77.  *    MS-Shell 2.0 Baseline release
  78.  *
  79.  */
  80.  
  81. #include <sys/types.h>
  82. #include <sys/stat.h>
  83. #if defined (__EMX__)
  84. #  include <sys/wait.h>
  85. #endif
  86. #include <stdio.h>
  87. #include <signal.h>
  88. #include <errno.h>
  89. #include <setjmp.h>
  90. #include <ctype.h>
  91. #include <string.h>
  92. #include <unistd.h>
  93. #include <stdlib.h>
  94. #include <fcntl.h>
  95. #include <limits.h>
  96. #include <dirent.h>
  97. #include <stdarg.h>
  98. #include <time.h>
  99. #include "sh.h"
  100.  
  101. #define    SECS        60L
  102. #define    MINS        3600L
  103.  
  104. #if (OS_TYPE == OS_OS2)
  105. #  if defined (__EMX__)
  106. #    define  PGM_TITLE_TYPE    unsigned char
  107. #  else
  108. #    define  PGM_TITLE_TYPE    char
  109. #  endif
  110. #endif
  111. /* Definitions for echo and print */
  112.  
  113. #define ECHO_ESCAPE    0x01
  114. #define ECHO_NO_EOL    0x02
  115. #define ECHO_HISTORY    0x04
  116.  
  117. /* Definitions for test */
  118.  
  119. #define END_OF_INPUT    0
  120. #define FILE_READABLE    1
  121. #define FILE_WRITABLE    2
  122. #define FILE_REGULAR    3
  123. #define FILE_DIRECTRY    4
  124. #define FILE_NONZERO    5
  125. #define FILE_TERMINAL    6
  126. #define STRING_ZERO    7
  127. #define STRING_NONZERO    8
  128. #define STRING_EQUAL    9
  129. #define STRING_NOTEQUAL    10
  130. #define NUMBER_EQUAL    11
  131. #define NUMBER_NOTEQUAL    12
  132. #define NUMBER_EQ_GREAT    13
  133. #define NUMBER_GREATER    14
  134. #define NUMBER_EQ_LESS    15
  135. #define NUMBER_LESS    16
  136. #define UNARY_NOT    17
  137. #define BINARY_AND    18
  138. #define BINARY_OR    19
  139. #define LPAREN        20
  140. #define RPAREN        21
  141. #define OPERAND        22
  142. #define FILE_EXECUTABLE    23
  143. #define FILE_USER    24
  144. #define FILE_GROUP    25
  145. #define FILE_TEXT    26
  146. #define FILE_BLOCK    27
  147. #define FILE_CHARACTER    28
  148. #define FILE_FIFO    29
  149. #define FILE_NEWER    30
  150. #define FILE_OLDER    31
  151. #define STRING_LESS    32
  152. #define STRING_GREATER    33
  153. #define FILE_EXISTS    34
  154. #define TEST_OPTION    35
  155. #define FILE_SYMBOLIC    36
  156. #define FILE_OWNER    37
  157. #define FILE_GROUPER    38
  158. #define FILE_SOCKET    39
  159. #define FILE_EQUAL    40
  160.  
  161. #define UNARY_OP    1
  162. #define BINARY_OP    2
  163. #define B_UNARY_OP    3
  164. #define B_BINARY_OP    4
  165. #define PAREN        5
  166.  
  167. /* This is the list of operators and the conversion values */
  168.  
  169. static struct TestOperator {
  170.     char    *OperatorName;
  171.     short     OperatorID;
  172.     short     OperatorType;
  173. } ListOfTestOperators[] = {
  174.  
  175. /* These two entries are modified depending on the test program. The
  176.  * alternative values are shown in the following comment.
  177.  */
  178.  
  179.     {"-a",    FILE_EXISTS,        UNARY_OP},
  180.     /* {"-a",    BINARY_AND,        B_BINARY_OP}, */
  181.     {"-o",    TEST_OPTION,        UNARY_OP},
  182.     /* {"-o",    BINARY_OR,        B_BINARY_OP}, */
  183.  
  184. /* Add new entries after here */
  185.  
  186.     {"-r",    FILE_READABLE,        UNARY_OP},
  187.     {"-w",    FILE_WRITABLE,        UNARY_OP},
  188.     {"-x",    FILE_EXECUTABLE,    UNARY_OP},
  189.     {"-f",    FILE_REGULAR,        UNARY_OP},
  190.     {"-d",    FILE_DIRECTRY,        UNARY_OP},
  191.     {"-s",    FILE_NONZERO,        UNARY_OP},
  192.     {"-t",    FILE_TERMINAL,        UNARY_OP},
  193.     {"-z",    STRING_ZERO,        UNARY_OP},
  194.     {"-n",    STRING_NONZERO,        UNARY_OP},
  195.     {"=",    STRING_EQUAL,        BINARY_OP},
  196.     {"!=",    STRING_NOTEQUAL,    BINARY_OP},
  197.     {"<",    STRING_LESS,        BINARY_OP},
  198.     {">",    STRING_GREATER,        BINARY_OP},
  199.     {"-eq",    NUMBER_EQUAL,        BINARY_OP},
  200.     {"-ne",    NUMBER_NOTEQUAL,    BINARY_OP},
  201.     {"-ge",    NUMBER_EQ_GREAT,    BINARY_OP},
  202.     {"-gt",    NUMBER_GREATER,        BINARY_OP},
  203.     {"-le",    NUMBER_EQ_LESS,        BINARY_OP},
  204.     {"-lt",    NUMBER_LESS,        BINARY_OP},
  205.     {"!",    UNARY_NOT,        B_UNARY_OP},
  206.     {"(",    LPAREN,            PAREN},
  207.     {")",    RPAREN,            PAREN},
  208.     {"&&",    BINARY_AND,        B_BINARY_OP},
  209.     {"||",    BINARY_OR,        B_BINARY_OP},
  210.     {"-c",    FILE_CHARACTER,        UNARY_OP},
  211.     {"-b",    FILE_BLOCK,        UNARY_OP},
  212.     {"-u",    FILE_USER,        UNARY_OP},
  213.     {"-g",    FILE_GROUP,        UNARY_OP},
  214.     {"-k",    FILE_TEXT,        UNARY_OP},
  215.     {"-p",    FILE_FIFO,        UNARY_OP},
  216.     {"-h",    FILE_SYMBOLIC,        UNARY_OP},
  217.     {"-L",    FILE_SYMBOLIC,        UNARY_OP},
  218.     {"-O",    FILE_OWNER,        UNARY_OP},
  219.     {"-G",    FILE_GROUPER,        UNARY_OP},
  220.     {"-S",    FILE_SOCKET,        UNARY_OP},
  221.     {"-nt",    FILE_NEWER,        BINARY_OP},
  222.     {"-ot",    FILE_OLDER,        BINARY_OP},
  223.     {"-ef",    FILE_EQUAL,        BINARY_OP},
  224.     {(char *)NULL,    0,        0}
  225. };
  226.  
  227. /*
  228.  * -o values for set
  229.  */
  230.  
  231. static struct SetOptions {
  232.     char        *OptionName;        /* Option name        */
  233.     unsigned int    FlagValue;        /* Option flag        */
  234.     bool        HasOptionValue;        /* Has -x value        */
  235. } SetOptions[] = {
  236.     { "alternation",    FLAGS_ALTERNATION,    FALSE },
  237.     { "allexport",    'a',            TRUE },
  238.  
  239. #ifdef FLAGS_BREAK_SWITCH
  240.     { "break",        FLAGS_BREAK_SWITCH,    FALSE },
  241. #endif
  242.  
  243. #ifdef FLAGS_EMACS
  244.     { "emacs",        FLAGS_EMACS,        FALSE },
  245. #endif
  246.  
  247.     { "errexit",    'e',            TRUE },
  248.  
  249. #ifdef FLAGS_GMACS
  250.     { "gmacs",        FLAGS_GMACS,        FALSE },
  251. #endif
  252.  
  253. #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_OS2)
  254.     { "ignorecase",    FLAGS_NOCASE,        FALSE },
  255. #endif
  256.  
  257.     { "ignoreeof",    FLAGS_IGNOREEOF,    FALSE },
  258.     { "keyword",    'k',            TRUE },
  259.     { "markdirs",    FLAGS_MARKDIRECTORY,    FALSE },
  260.     { "msdos",        FLAGS_MSDOS_FORMAT,    FALSE },
  261.     { "noclobber",    FLAGS_NOCLOBER,        FALSE },
  262.     { "noexec",        'n',            TRUE },
  263.     { "noglob",        'f',            TRUE },
  264.     { "nounset",    'u',            TRUE },
  265.  
  266. #ifdef FLAGS_SET_OS2
  267.     { "os2",        FLAGS_SET_OS2,        FALSE },
  268. #endif
  269.  
  270.     { "privileged",    'p',            TRUE },
  271.  
  272. #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_OS2)
  273.     { "realpipes",    FLAGS_REALPIPES,    FALSE },
  274. #endif
  275.  
  276.     { "trackall",    'h',            TRUE },
  277.  
  278. #ifdef FLAGS_VI
  279.     { "vi",        FLAGS_VI,        FALSE },
  280. #endif
  281.  
  282.     { "verbose",    'v',            TRUE },
  283.     { "verify",        FLAGS_VERIFY_SWITCH,    FALSE },
  284.  
  285. #ifdef FLAGS_SET_NT
  286.     { "winnt",        FLAGS_SET_NT,        FALSE },
  287. #endif
  288.  
  289.     { "xtrace",        'x',            TRUE },
  290.     { (char *)NULL,    0,    FALSE }
  291. };
  292.  
  293. /*
  294.  * Getopts values
  295.  */
  296.  
  297. static GetoptsIndex    GetOptsIndex = { 1, 1 };
  298.  
  299. /*
  300.  * Signal Name structure
  301.  *
  302.  * Note that the first two entries are constructed such that the character
  303.  * before the name is a ~.
  304.  */
  305.  
  306. #define MAX_TRAP_SIGNALS    ARRAY_SIZE (TrapSignalList)
  307. #define SIG_SPECIAL        -1        /* Error or trap */
  308. #define SIG_NO_MAP        -2        /* No DOS mapping */
  309.  
  310. #if (OS_TYPE == OS_UNIX)
  311. #define MAX_SIG_MAP        NSIG
  312. #else
  313. #define MAX_SIG_MAP        ARRAY_SIZE (UnixToDosSignals)
  314. #endif
  315.  
  316. /*
  317.  * Signal name to number mapping
  318.  */
  319.  
  320. static struct TrapSignalList {
  321.     char    *name;
  322.     int        signo;
  323. } TrapSignalList [] = {
  324.     { Trap_DEBUG + 1,        SIG_SPECIAL },
  325.     { Trap_ERR + 1,        SIG_SPECIAL },
  326.     { LIT_exit,            0 },
  327.     { "SIGINT",            SIGINT },
  328.     { "SIGFPE",            SIGFPE },
  329.     { "SIGILL",            SIGILL },
  330.     { "SIGSEGV",        SIGSEGV },
  331.     { "SIGABRT",        SIGABRT },
  332.  
  333. #ifdef SIGTERM
  334.     { "SIGTERM",        SIGTERM },
  335. #endif
  336.  
  337. #ifdef SIGBREAK
  338.     { "SIGBREAK",        SIGBREAK },
  339. #endif
  340.  
  341. #ifdef SIGUSR1
  342.     { "SIGUSR1",        SIGUSR1 },
  343. #endif
  344.  
  345. #ifdef SIGUSR2
  346.     { "SIGUSR2",        SIGUSR2 },
  347. #endif
  348.  
  349. #ifdef SIGUSR3
  350.     { "SIGUSR3",        SIGUSR3 },
  351. #endif
  352.  
  353. #ifdef SIGIDIVZ
  354.     { "SIGIDIVZ",        SIGIDIVZ },
  355. #endif
  356.  
  357. #ifdef SIGIOVFL
  358.     { "SIGIOVFL",        SIGIOVFL },
  359. #endif
  360.  
  361. #if (OS_TYPE == OS_UNIX)
  362.     { "SIGHUP",            SIGHUP },
  363.     { "SIGQUIT",        SIGQUIT },
  364.     { "SIGTRAP",        SIGTRAP },
  365.     { "SIGIOT",            SIGIOT },
  366.     { "SIGEMT",            SIGEMT },
  367.     { "SIGKILL",        SIGKILL },
  368.     { "SIGBUS",            SIGBUS },
  369.     { "SIGSYS",            SIGSYS },
  370.     { "SIGPIPE",        SIGPIPE },
  371.     { "SIGALRM",        SIGALRM },
  372.     { "SIGTERM",        SIGTERM },
  373.     { "SIGUSR1",        SIGUSR1 },
  374.     { "SIGUSR2",        SIGUSR2 },
  375.     { "SIGCLD",            SIGCLD },
  376.     { "SIGPWR",            SIGPWR },
  377.     { "SIGWINCH",        SIGWINCH },
  378.     { "SIGURG",            SIGURG },
  379.     { "SIGPOLL",        SIGPOLL },
  380.     { "SIGIO",            SIGIO },
  381.     { "SIGSTOP",        SIGSTOP },
  382.     { "SIGTSTP",        SIGTSTP },
  383.     { "SIGCONT",        SIGCONT },
  384.     { "SIGTTIN",        SIGTTIN },
  385.     { "SIGTTOU",        SIGTTOU },
  386.     { "SIGVTALRM",        SIGVTALRM },
  387.     { "SIGPROF",        SIGPROF },
  388.     { "SIGXCPU",        SIGXCPU },
  389.     { "SIGXFSZ",        SIGXFSZ },
  390. #endif
  391. };
  392.  
  393. /*
  394.  * UNIX to DOS signal number mapping.  We only have 15 mappings because
  395.  * only the fdirst 15 signal numbers are common
  396.  */
  397.  
  398. #if (OS_TYPE != OS_UNIX)
  399. static int    UnixToDosSignals [] = {
  400.     0,            /* 0 - On exit                */
  401.     SIG_NO_MAP,        /* 1 - hangup                */
  402.     SIGINT,        /* 2 - interrupt (DEL)            */
  403.     SIG_NO_MAP,        /* 3 - quit (ASCII FS)            */
  404.     SIGILL,        /* 4 - illegal instruction        */
  405.     SIG_NO_MAP,        /* 5 - trace trap            */
  406.     SIG_NO_MAP,        /* 6 - IOT instruction            */
  407.     SIG_NO_MAP,        /* 7 - EMT instruction            */
  408.     SIGFPE,        /* 8 - floating point exception        */
  409.     SIG_NO_MAP,        /* 9 - kill                */
  410.     SIG_NO_MAP,        /* 10 - bus error            */
  411.     SIGSEGV,        /* 11 - segmentation violation        */
  412.     SIG_NO_MAP,        /* 12 - bad argument to system call    */
  413.     SIG_NO_MAP,        /* 13 - write on a pipe with no reader    */
  414.     SIG_NO_MAP,        /* 14 - alarm clock            */
  415.     SIGTERM        /* 15 - software termination signal    */
  416. };
  417. #endif
  418.  
  419.  
  420. /*
  421.  * General Functions
  422.  */
  423.  
  424. static int        DeleteAllVariables (const void *, const void *);
  425. static int F_LOCAL    doOutofMemory (char *);
  426. static int F_LOCAL    TestProcessNextExpression (int);
  427. static int F_LOCAL    TestANDExpression (int);
  428. static int F_LOCAL    TestPrimaryExpression (int);
  429. static int F_LOCAL    TestUnaryOperand (int);
  430. static int F_LOCAL    TestBinaryOperand (void);
  431. static int F_LOCAL    TestLexicalAnalysis (void);
  432. static struct TestOperator *
  433.        F_LOCAL    TestLookupOperator (char *);
  434. static long F_LOCAL    GetNumberForTest (char *);
  435. static void F_LOCAL    TestSyntaxError (void);
  436. static void F_LOCAL    SetVerifyStatus (bool);
  437. static void F_LOCAL    WhenceLocation (bool, char *, char *);
  438. static void F_LOCAL    WhenceType (char *);
  439. static int F_LOCAL    PrintOptionSettings (void);
  440. static int F_LOCAL    CheckFAccess (char *, int);
  441. static int F_LOCAL    CheckFType (char *, mode_t);
  442. static int F_LOCAL    CheckFMode (char *, mode_t);
  443. static int F_LOCAL    CheckFSize (char *);
  444. static int F_LOCAL    CheckForFD (char *);
  445.  
  446. #if (OS_TYPE != OS_UNIX)
  447. static OSCALL_RET F_LOCAL OS_GetFHAttributes (int, OSCALL_PARAM *);
  448. #endif
  449.  
  450. #if (OS_TYPE == OS_DOS)
  451. static void F_LOCAL    SetBreakStatus (bool);
  452. #else
  453. #  define SetBreakStatus(a)
  454. #endif
  455.  
  456. static bool F_LOCAL    CheckPhysLogical (char *, bool *);
  457. static char * F_LOCAL    GetPhysicalPath (char *, bool);
  458. static bool F_LOCAL    ReadALine (int, char *, bool, bool, int *);
  459. static bool F_LOCAL    WriteOutLine (int);
  460. static bool F_LOCAL    ChangeOptionValue (char *, bool);
  461. static void F_LOCAL    SetClearFlag (int, bool);
  462. static void F_LOCAL    RemoveVariable (char *, int);
  463. static int F_LOCAL    BreakContinueProcessing (char *, int);
  464. static int F_LOCAL    SetUpNewParameterVariables (char **, int, int, char *);
  465. static int F_LOCAL    UsageError (char *);
  466. static void F_LOCAL     PrintEntry (VariableList *, bool, unsigned int);
  467. static int F_LOCAL     UpdateVariableStatus (char **, unsigned int);
  468. static int F_LOCAL    TypesetVariables (char **);
  469. static int F_LOCAL    ListAllVariables (unsigned int, bool);
  470. static int F_LOCAL    HandleFunction (char *);
  471. static int F_LOCAL    TestOptionValue (char *, bool);
  472. static int F_LOCAL    GetUnitNumber (char *);
  473. static struct SetOptions * F_LOCAL LookUpOptionValue (char *);
  474. static void        DisplayVariableEntry (const void *, VISIT, int);
  475. static struct TrapSignalList * F_LOCAL LookupSignalName (char *);
  476.  
  477. #if (OS_TYPE != OS_DOS)
  478. static bool F_LOCAL    ConvertJobToPid (char *, PID *);
  479.  
  480. #  if (OS_TYPE == OS_OS2)
  481. static void F_LOCAL    DisplayStartData (STARTDATA *);
  482. #  endif
  483.  
  484. #  if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32)
  485. APIRET             DosQFileMode (PSZ, PULONG);
  486.  
  487. #    if !defined (__EMX__)
  488. #      define Dos32FlagProcess        DosFlagProcess
  489. #      pragma linkage (DosFlagProcess, far16 pascal)
  490. #    else
  491. USHORT    _THUNK_FUNCTION (Dos16FlagProcess) ();
  492. #    endif
  493.  
  494. extern USHORT        Dos32FlagProcess (PID, USHORT, USHORT, USHORT);
  495.  
  496. #  endif
  497.  
  498. #  if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_16)
  499. #      define Dos32FlagProcess        DosFlagProcess
  500. #  endif
  501.  
  502. #  if (OS_TYPE == OS_NT)
  503. int             DosQFileMode (char *, DWORD *);
  504. #  endif
  505. #endif
  506.  
  507. /*
  508.  * Builtin Commands
  509.  */
  510.  
  511. static int    doexport (int, char **);
  512. static int    doreadonly (int, char **);
  513. static int    domsdos (int, char **);
  514. static int    dotypeset (int, char **);
  515. static int    dounalias (int, char **);
  516. static int    doalias (int, char **);
  517. static int    dolabel (int, char **);
  518. static int    dofalse (int, char **);
  519. static int    dochdir (int, char **);
  520. static int    dodrive (int, char **);
  521. static int    doshift (int, char **);
  522. static int    doumask (int, char **);
  523. static int    dodot (int, char **);
  524. static int    doecho (int, char **);
  525. static int    dolet (int, char **);
  526. static int    doshellinfo (int, char **);
  527.  
  528. #if (OS_TYPE == OS_OS2)
  529. static int    dostart (int, char **);
  530. #endif
  531.  
  532. #if (OS_TYPE != OS_DOS)
  533. static int    dodetach (int, char **);
  534. static int    dokill (int, char **);
  535. static int    dojobs (int, char **);
  536. static int    dowait (int, char **);
  537. #endif
  538.  
  539. #if defined (FLAGS_EMACS) || defined (FLAGS_GMACS)
  540. static int    dobind (int, char **);
  541. #endif
  542.  
  543. #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32) && !defined (__WATCOMC__)
  544. static int    dotimes (int, char **);
  545. #endif
  546.  
  547. #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_UNIX)
  548. static int    dotimes (int, char **);
  549. #endif
  550.  
  551. static int    dogetopts (int, char **);
  552. static int    dopwd (int, char **);
  553. static int    doswap (int, char **);
  554. static int    dounset (int, char **);
  555. static int    dowhence (int, char **);
  556. static int    dofc (int, char **);
  557. static int    dotest (int, char **);
  558. static int    dover (int, char **);
  559. static int    doread (int, char **);
  560. static int    doeval (int, char **);
  561. static int    dotrap (int, char **);
  562. static int    dobuiltin (int, char **);
  563. static int    dobreak (int, char **);
  564. static int    docontinue (int, char **);
  565. static int    doexit (int, char **);
  566. static int    doreturn (int, char **);
  567. static int    doset (int, char **);
  568. static int    dofunctions (int, char **);
  569. static int    dohistory (int, char **);
  570.  
  571. /*
  572.  * TWALK global values for DisplayVariable
  573.  */
  574.  
  575. static unsigned int    DVE_Mask;
  576. static bool         DVE_PrintValue;
  577.  
  578. /*
  579.  * Local data structure for test command
  580.  */
  581.  
  582. static char            **TestArgumentList;
  583. static struct TestOperator    *CurrentTestOperator;
  584. static jmp_buf            TestErrorReturn;
  585. static char            *TestProgram;
  586. static bool            NewTestProgram;
  587.  
  588. /*
  589.  * Static structure for typeset
  590.  */
  591.  
  592. static struct TypesetValues {
  593.     unsigned int    Flags_On;
  594.     unsigned int    Flags_Off;
  595.     int            Base;
  596.     int            Width;
  597. } TypesetValues;
  598.  
  599. /*
  600.  * Current position in Getoption string
  601.  */
  602.  
  603. static int    GetOptionPosition = 1;        /* Current position    */
  604. static int    BadOptionValue = 0;        /* The bad option value    */
  605.  
  606. /*
  607.  * Common strings
  608.  */
  609.  
  610. static char    *DoubleHypen = "--";
  611. static char    *TypeSetUsage = "typeset [ [ [+|-][Hflprtux] ] [+|-][LRZi[n]] [ name [=value] ...]";
  612. static char    *NotBuiltinCommand = "not a builtin";
  613. static char    *NotAnAlias = "%s: %s is not an alias";
  614. static char    *NotValidAlias = "Invalid alias name";
  615. static char    *Reply_Array[] = {LIT_REPLY, (char *)NULL};
  616. static char    *BadDrive = "%c: bad drive";
  617. static char    *ShellInternalCommand = "is a shell internal command";
  618. static char    *FCTooLong = "fc: command line too long";
  619. static char    LIT_alias[] = "alias";
  620. static char    LIT_print[] = "print";
  621. static char    LIT_read[] = "read";
  622. static char    LIT_shift[] = "shift";
  623. static char    LIT_break[] = "break";
  624. static char    LIT_builtin[] = "builtin";
  625. static char    LIT_devfd[] = "/dev/fd/";
  626. static char    *BuiltinUsage = "builtin [ -ads ] [ commands ]";
  627. static char    *WhenceUsage = "whence [ -pvt ] [ commands ]";
  628. static char    LIT_continue[] = "continue";
  629. static char    LIT_type[] = "type";
  630. static char    LIT_unalias[] = "unalias";
  631. static char    LIT_unfunction[] = "unfunction";
  632. static char    *LIT_bun = "bad unit number";
  633. static char    *HistoryUsage = "history [ -iedsl ] [ number ]";
  634. static char    *ReturnUsage = "return [ value ]";
  635.  
  636. #if (OS_TYPE == OS_OS2)
  637. static char    *StartUsage =
  638. "start -O [dos | pm] [ -hHfWPFxibID ] [ -c [ vilsna ]] [ -t title ]\n                   [ -e string ]\n       start [ -dfWPFibCISxhH ] [ -c [ vilsna ]] [ -t title ] [ -e string ]\n                   [ -X directory ] [ args.. ]\n       start -A sessionId";
  639. static char    *Start_NoSession = "start: Cannot switch to session %lu\n%s";
  640. #endif
  641.  
  642. #if (OS_TYPE != OS_DOS)
  643. static char    *JobUsage = "jobs [-lp] [ -P pid]";
  644. static char    *KillUsage = "kill [ [-l] | [ [-sig] [ process id | %job number ] ... ] ]";
  645. #endif
  646.  
  647. /*
  648.  * Disable variables mapping
  649.  */
  650.  
  651. struct    DisableVariableMap {
  652.     char    *name;
  653.     int        flag;
  654. } DisableVariableMap [] = {
  655.     { MailCheckVariable,    DISABLE_MAILCHECK },
  656.     { OptArgVariable,        DISABLE_OPTARG },
  657.     { OptIndVariable,        DISABLE_OPTIND },
  658.     { SecondsVariable,        DISABLE_SECONDS },
  659.     { RandomVariable,        DISABLE_RANDOM },
  660.     { LastWordVariable,        DISABLE_LASTWORD },
  661.     { LineCountVariable,    DISABLE_LINECOUNT },
  662.  
  663. #if (OS_TYPE != OS_DOS)
  664.     { WinTitleVariable,        DISABLE_WINTITLE },
  665. #endif
  666.     { (char *)NULL,        0 },
  667. };
  668.  
  669. /*
  670.  * built-in commands: : and true
  671.  */
  672.  
  673. static int dolabel (int argc, char **argv)
  674. {
  675.     return 0;
  676. }
  677.  
  678. static int dofalse (int argc, char **argv)
  679. {
  680.     return 1;
  681. }
  682.  
  683. /*
  684.  * Getopts - split arguments.  getopts optstring name [ arg ... ]
  685.  */
  686.  
  687. static int dogetopts (int argc, char **argv)
  688. {
  689.     char    **Arguments;
  690.     char    *OptionString;
  691.     int        result;
  692.     char    SResult[3];
  693.     char    BadResult[2];
  694.     int        Mode = GETOPT_MESSAGE | GETOPT_PLUS;
  695.  
  696.     if (argc < 3)
  697.     return UsageError ("getopts optstring name [ arg ... ]");
  698.  
  699.     memset (SResult, 0, 3);
  700.  
  701. /*
  702.  * A leading : in optstring causes getopts to store the letter of an
  703.  * invalid option in OPTARG, and to set name to ? for an unknown option and
  704.  * to : when a required option is missing.
  705.  */
  706.  
  707.     if (*(OptionString = argv[1]) == ':')
  708.     {
  709.     OptionString++;
  710.     Mode = GETOPT_PLUS;
  711.     }
  712.  
  713. /*
  714.  * Use positional parameters
  715.  */
  716.  
  717.     if (argc == 3)
  718.     {
  719.     argc = ParameterCount + 1;
  720.     Arguments = ParameterArray;
  721.     }
  722.  
  723. /* No - use supplied */
  724.  
  725.     else
  726.     {
  727.     Arguments = &argv[2];        /* Adjust pointers        */
  728.     argc -= 2;
  729.     }
  730.  
  731. /*
  732.  * Get the value of OPTIND and initialise the getopt function
  733.  */
  734.  
  735.     if (!(DisabledVariables & DISABLE_OPTIND))
  736.     OptionIndex = (int)GetVariableAsNumeric (OptIndVariable);
  737.  
  738.     else
  739.     OptionIndex = GetOptsIndex.Index;
  740.  
  741. /* Initialise the other values */
  742.  
  743.     GetOptionPosition = GetOptsIndex.SubIndex;
  744.     OptionArgument = (char *)NULL;
  745.  
  746.     result = GetOptions (argc, Arguments, OptionString, Mode);
  747.  
  748. /* Save new positions */
  749.  
  750.     SaveGetoptsValues (OptionIndex, GetOptionPosition);
  751.  
  752. /* Check for EOF */
  753.  
  754.     if (result == EOF)
  755.     return 1;
  756.  
  757. /* Set up result string */
  758.  
  759.     *SResult = (char)result;
  760.  
  761. /* Did we get an error.  Yes.  If message output, don't put value
  762.  * in OPTARG
  763.  */
  764.  
  765.     if (result == '?')
  766.     {
  767.     if (Mode & GETOPT_MESSAGE)
  768.         OptionArgument = (char *)NULL;
  769.  
  770. /* Error, set up values in optarg and the result */
  771.  
  772.     else
  773.     {
  774.         SResult[0] = (char)((OptionArgument == (char *)NULL) ? '?' : ':');
  775.         *(OptionArgument = BadResult) = (char)BadOptionValue;
  776.         *(OptionArgument + 1) = 0;
  777.     }
  778.     }
  779.  
  780. /* If the argument started with a +, tell them */
  781.  
  782.     else if (OptionStart == '+')
  783.     {
  784.       SResult[1] = (char)result;
  785.       SResult[0] = '+';
  786.     }
  787.  
  788. /* If we got an argument, set the various shell variables */
  789.  
  790.     if ((OptionArgument != (char *)NULL) &&
  791.     (!(DisabledVariables & DISABLE_OPTARG)))
  792.     SetVariableFromString (OptArgVariable, OptionArgument);
  793.  
  794.     SetVariableFromString (argv[2], SResult);
  795.     return 0;
  796. }
  797.  
  798. /*
  799.  * Reset the getopts values
  800.  */
  801.  
  802. void    ResetGetoptsValues (bool Variable)
  803. {
  804.     if (Variable && (!(DisabledVariables & DISABLE_OPTIND)))
  805.     SetVariableFromNumeric (OptIndVariable, 1L);
  806.  
  807.     GetOptsIndex.Index = 1;
  808.     GetOptsIndex.SubIndex = 1;
  809. }
  810.  
  811. /*
  812.  * Save the new Getopts values
  813.  */
  814.  
  815. void    SaveGetoptsValues (int Index, int Position)
  816. {
  817.     if (!(DisabledVariables & DISABLE_OPTIND))
  818.     SetVariableFromNumeric (OptIndVariable, (long)Index);
  819.  
  820.     GetOptsIndex.Index = Index;
  821.     GetOptsIndex.SubIndex = Position;
  822. }
  823.  
  824. /*
  825.  * Get the current Getopts values
  826.  */
  827.  
  828. void    GetGetoptsValues (GetoptsIndex *values)
  829. {
  830.     values->Index = GetOptsIndex.Index;
  831.     values->SubIndex = GetOptsIndex.SubIndex;
  832. }
  833.  
  834. /*
  835.  * Echo the parameters:  echo [ -n ] parameters
  836.  */
  837.  
  838. static int doecho (int argc, char **argv)
  839. {
  840.     int        flags = ECHO_ESCAPE;
  841.     int        fid = 1;
  842.     char    *ip;            /* Input pointer        */
  843.     char    *op;
  844.     int        c, c1;
  845.     int        R_flag = GETOPT_PRINT;    /* Enable -n test        */
  846.  
  847.     ResetGetOptions ();            /* Reset GetOptions        */
  848.  
  849. /* Echo or print? */
  850.  
  851.     if (strcmp (*argv, LIT_print) == 0)
  852.     {
  853.     R_flag = 0;            /* Reset        */
  854.  
  855.     while ((c = GetOptions (argc, argv, "Rnprsu:", R_flag)) != EOF)
  856.     {
  857.         switch (c)
  858.         {
  859.         case 'R':
  860.             R_flag = GETOPT_PRINT;
  861.             flags &= ~ECHO_ESCAPE;
  862.             break;
  863.  
  864.         case 'n':
  865.             flags = ECHO_NO_EOL;
  866.             break;
  867.  
  868.         case 'r':
  869.             flags &= ~ECHO_ESCAPE;
  870.             break;
  871.  
  872.         case 's':
  873.             flags |= ECHO_HISTORY;
  874.             break;
  875.  
  876.         case 'p':
  877.             break;
  878.  
  879.         case 'u':
  880.             if ((fid = GetUnitNumber (LIT_print)) == -1)
  881.             return 1;
  882.  
  883.             break;
  884.  
  885.         default:
  886.             return UsageError ("print [ -Rpnrsu[unit] ] ...");
  887.         }
  888.     }
  889.     }
  890.  
  891.     if ((OptionIndex < argc) && (R_flag == GETOPT_PRINT) &&
  892.     (!strcmp (argv[OptionIndex], "-n")))
  893.     {
  894.     flags |= ECHO_NO_EOL;
  895.     ++OptionIndex;
  896.     }
  897.  
  898.     argv += OptionIndex;
  899.  
  900. /* Clear the history buffer so we can use it */
  901.  
  902.     FlushHistoryBuffer ();
  903.     op = ConsoleLineBuffer;
  904.  
  905. /* Process the arguments.  Process \ as a special if necessary */
  906.  
  907.     while ((ip = *(argv++)) != NOWORD)
  908.     {
  909.  
  910. /* Process the character */
  911.  
  912.     while ((c = (int)(*(ip++))))
  913.     {
  914.  
  915. /* If echo too big - disable history save */
  916.  
  917.         if ((op - ConsoleLineBuffer) > LINE_MAX - 4)
  918.         {
  919.         *op = 0;
  920.  
  921.         if (!WriteOutLine (fid))
  922.             return 1;
  923.  
  924.         op = ConsoleLineBuffer;
  925.  
  926.         if (flags & ECHO_HISTORY)
  927.             fprintf (stderr, BasicErrorMessage,
  928.                  "Line too long for history", LIT_print);
  929.  
  930.         flags &= ~ECHO_HISTORY;
  931.         }
  932.  
  933.         if ((flags & ECHO_ESCAPE) && (c == CHAR_META))
  934.         {
  935.         c1 = *ip;
  936.  
  937.         if ((c = ProcessOutputMetaCharacters (&ip)) == -1)
  938.         {
  939.             flags |= ECHO_NO_EOL;
  940.             continue;
  941.         }
  942.  
  943. /* If unchanged - output backslash */
  944.  
  945.         else if ((c == c1) && (c != CHAR_META))
  946.             *(op++) = CHAR_META;
  947.         }
  948.  
  949.         *(op++) = (char)c;
  950.     }
  951.  
  952. /* End of string - check to see if a space if required */
  953.  
  954.     if (*argv != NOWORD)
  955.         *(op++) = CHAR_SPACE;
  956.     }
  957.  
  958. /* Is EOL required ? */
  959.  
  960.     if (!(flags & ECHO_NO_EOL))
  961.     {
  962. #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32)
  963.     if (IS_Console (fid))
  964.         *(op++) = CHAR_RETURN;
  965. #endif
  966.     *(op++) = CHAR_NEW_LINE;
  967.     }
  968.  
  969.     *op = 0;
  970.  
  971.     if (!WriteOutLine (fid))
  972.         return 1;
  973.  
  974. /* Save history */
  975.  
  976.     if (flags & ECHO_HISTORY)
  977.     {
  978.     CleanUpBuffer (op - ConsoleLineBuffer, ConsoleLineBuffer, 0x1a);
  979.     AddHistory (FALSE);
  980.     }
  981.  
  982.     return 0;
  983. }
  984.  
  985. /*
  986.  * Write out the current line
  987.  */
  988.  
  989. static bool F_LOCAL WriteOutLine (int fid)
  990. {
  991.     size_t    Len = strlen (ConsoleLineBuffer);
  992.  
  993.     if (write (fid, ConsoleLineBuffer, Len) != (int)Len)
  994.     {
  995.         PrintWarningMessage ("print: write error on unit %d\n", fid);
  996.     return FALSE;
  997.     }
  998.  
  999.     return TRUE;
  1000.  
  1001. }
  1002.  
  1003. /*
  1004.  * Display the current version: ver
  1005.  */
  1006.  
  1007. static int dover (int argc, char **argv)
  1008. {
  1009.     PrintVersionNumber (stdout);
  1010.     return 0;
  1011. }
  1012.  
  1013. #ifdef OS_SWAPPING
  1014. static char    *swap_device[] = {"disk", "extend", "expand"};
  1015. #endif
  1016.  
  1017. /*
  1018.  * Modify swapping information: swap [ options ]
  1019.  */
  1020.  
  1021. static int doswap (int argc, char **argv)
  1022. {
  1023. #if (OS_TYPE != OS_DOS)
  1024.     printf ("Swapping not available on %s", LIT_OSname);
  1025. #elif !defined (OS_SWAPPING)
  1026.     puts ("Swapping not available in 32-bit mode");
  1027. #else
  1028.     int        n = 1;
  1029.     char    *cp;
  1030.  
  1031. /* Display current values ? */
  1032.  
  1033.     if (argv[1] == NOWORD)
  1034.     {
  1035.     if (Swap_Mode == SWAP_OFF)
  1036.         puts ("Swapping disabled");
  1037.  
  1038.     else
  1039.     {
  1040.         int        j;
  1041.  
  1042.         foputs ("Swap devices: ");
  1043.  
  1044.         for (j = 0, n = 1; j < 3; ++j, n <<= 1)
  1045.         {
  1046.         if (Swap_Mode & n)
  1047.         {
  1048.             printf ("%s ", swap_device[j]);
  1049.  
  1050.             if (n == SWAP_EXTEND)
  1051.             printf ("(0x%.6lx) ", SW_EMstart);
  1052.         }
  1053.         }
  1054.  
  1055.         fputchar (CHAR_NEW_LINE);
  1056.     }
  1057.  
  1058.     return 0;
  1059.     }
  1060.  
  1061. /* Set up new values */
  1062.  
  1063.     Swap_Mode = SWAP_OFF;
  1064.     ClearSwapFile ();
  1065.  
  1066.     while ((cp = argv[n++]) != NOWORD)
  1067.     {
  1068.     if (strcmp (cp, "off") == 0)
  1069.         Swap_Mode = SWAP_OFF;
  1070.  
  1071.     else if (strcmp (cp, "on") == 0)
  1072.         Swap_Mode = SWAP_DISK | SWAP_EXPAND | SWAP_EXTEND;
  1073.  
  1074. /* Scan for valid arguments */
  1075.  
  1076.     else
  1077.     {
  1078.         int        j, k;
  1079.  
  1080.         for (j = 0, k = 1; j < 3; ++j, k <<= 1)
  1081.         {
  1082.         if (strcmp (cp, swap_device[j]) == 0)
  1083.         {
  1084.             Swap_Mode |= k;
  1085.  
  1086. /* If extended memory, they can specify the start address as a hex number */
  1087.  
  1088.             if (k == SWAP_EXTEND)
  1089.             {
  1090.             char    *sp;
  1091.             long    start;
  1092.  
  1093. /* Check for not changed */
  1094.  
  1095.             if ((sp = argv[n]) == NOWORD)
  1096.                 break;
  1097.  
  1098. /* Convert hex number */
  1099.  
  1100.             if (!ConvertNumericValue (sp, &start, 16))
  1101.                 break;
  1102.  
  1103. /* Set used and saved new value */
  1104.  
  1105.             SW_EMstart = start;
  1106.             ++n;
  1107.  
  1108.             if ((SW_EMstart < 0x100000L) ||
  1109.                 (SW_EMstart > 0xf00000L))
  1110.                 SW_EMstart = 0x100000L;
  1111.  
  1112.             printf ("Extend memory start set to 0x%.6lx\n",
  1113.                   SW_EMstart);
  1114.             }
  1115.  
  1116.             break;
  1117.         }
  1118.         }
  1119.  
  1120.         if (j == 3)
  1121.         return UsageError ("swap [ on | off | disk | expand | extend [ address ] ] ...");
  1122.     }
  1123.     }
  1124.  
  1125. #endif
  1126.     return 0;
  1127. }
  1128.  
  1129. /*
  1130.  * Output the current path: pwd [drives]
  1131.  */
  1132.  
  1133. static int dopwd (int argc, char **argv)
  1134. {
  1135.     int            i;
  1136.     int            Start = 1;
  1137.     int            RetVal = 0;
  1138.     char        *sp;
  1139.     char        ndrive;
  1140.     char        ldir[PATH_MAX + 6];
  1141.     bool        Physical = FALSE;
  1142.  
  1143.     if ((argc > 1) && CheckPhysLogical (argv[1], &Physical))
  1144.     Start++;
  1145.  
  1146. /* Print the current directories on the selected drive */
  1147.  
  1148.     i = Start;
  1149.  
  1150.     while ((sp = argv[i++]) != NOWORD)
  1151.     {
  1152.  
  1153. /* Select the drive and get the path */
  1154.  
  1155.     while ((ndrive = *(sp++)))
  1156.     {
  1157.         errno = 0;
  1158.  
  1159.         if (!S_getcwd (ldir, GetDriveNumber (ndrive)) || errno)
  1160.         RetVal = PrintWarningMessage (BadDrive, ndrive);
  1161.  
  1162.         else if (Physical)
  1163.         printf ("%c: path is %s\n", tolower (ndrive),
  1164.             GetPhysicalPath (ldir, TRUE));
  1165.  
  1166.         else
  1167.         puts (ldir);
  1168.     }
  1169.     }
  1170.  
  1171. /* Print the current directory */
  1172.  
  1173.     if (argv[Start] == NOWORD)
  1174.     {
  1175.     strcpy (ldir, CurrentDirectory->value);
  1176.     puts ((Physical) ? GetPhysicalPath (ldir, TRUE) : ldir);
  1177.     }
  1178.  
  1179.     return RetVal;
  1180. }
  1181.  
  1182. /*
  1183.  * Unset a variable: unset [ flag ] [ variable name... ]
  1184.  * Delete a function: unfunction <names ...>
  1185.  */
  1186.  
  1187. static int dounset (int argc, char **argv)
  1188. {
  1189.     int            n = 1;
  1190.     bool        Fnc = FALSE;
  1191.     FunctionList     *fp;
  1192.     char        *cp;
  1193.     int            i;
  1194.  
  1195. /* -f, functions */
  1196.  
  1197.     if (strcmp (*argv, LIT_unfunction) == 0)
  1198.     Fnc = TRUE;
  1199.  
  1200.     else if ((argc > 1) && (strcmp (argv[1], "-f") == 0))
  1201.     {
  1202.     n++;
  1203.     Fnc = TRUE;
  1204.     }
  1205.  
  1206. /* Unset the variables, flags or functions */
  1207.  
  1208.     while ((cp = argv[n++]) != NOWORD)
  1209.     {
  1210.     if (!Fnc)
  1211.     {
  1212.         UnSetVariable (cp, -1, FALSE);
  1213.  
  1214.         for (i = 0; DisableVariableMap[i].name != (char *)NULL; i++)
  1215.         {
  1216.         if (strcmp (DisableVariableMap[i].name, cp) == 0)
  1217.         {
  1218.             DisabledVariables |= DisableVariableMap[i].flag;
  1219.             break;
  1220.         }
  1221.         }
  1222.     }
  1223.  
  1224.     else if ((fp = LookUpFunction (cp, TRUE)) != (FunctionList *)NULL)
  1225.         DeleteFunction (fp->tree);
  1226.     }
  1227.  
  1228.     return 0;
  1229. }
  1230.  
  1231. /* Delete a variable.  If all is set, system variables can be deleted.
  1232.  * This is used to delete the trap functions
  1233.  */
  1234.  
  1235. void    UnSetVariable (char *cp,    /* Variable name        */
  1236.                int  OIndex,    /* Index preprocessed value    */
  1237.                bool all)    /* Any variable            */
  1238. {
  1239.     long        Index;
  1240.     char        *term;
  1241.     bool        ArrayDetected;
  1242.  
  1243. /* Unset a flag */
  1244.  
  1245.     if (*cp == '-')
  1246.     {
  1247.     while (*(++cp) != 0)
  1248.     {
  1249.         if (islower (*cp))
  1250.         FL_CLEAR (*cp);
  1251.     }
  1252.  
  1253.     SetShellSwitches ();
  1254.     return;
  1255.     }
  1256.  
  1257. /* Ok - unset a variable and not a local value */
  1258.  
  1259.     if (!all && !(IS_VariableFC ((int)*cp)))
  1260.     return;
  1261.  
  1262. /* Check in list */
  1263.  
  1264.     if (!GetVariableName (cp, &Index, &term, &ArrayDetected) || *term)
  1265.     return;
  1266.  
  1267.     if (OIndex != -1)
  1268.     Index = OIndex;
  1269.  
  1270. /*  Delete a specific entry or all? */
  1271.  
  1272.     if (!((ArrayDetected || (OIndex != -1))))
  1273.     Index = -1;
  1274.  
  1275.     RemoveVariable (cp, (int)Index);
  1276. }
  1277.  
  1278. /*
  1279.  * Delete a variable
  1280.  *
  1281.  * Index = -1 implies all references
  1282.  */
  1283.  
  1284. static void F_LOCAL RemoveVariable (char *name,    /* Variable name    */
  1285.                     int  Index)    /* Array index        */
  1286. {
  1287.     VariableList    **vpp;
  1288.     VariableList    *vp;
  1289.     VariableList    dummy;
  1290.     void        (*save_signal)(int);
  1291.  
  1292.     while (TRUE)
  1293.     {
  1294.     dummy.name = name;
  1295.     dummy.index = (int)Index;
  1296.  
  1297.     vpp = (VariableList **)tfind (&dummy, &VariableTree,
  1298.                       DeleteAllVariables);
  1299.  
  1300. /* If not found, Ignore unset request */
  1301.  
  1302.     if (vpp == (VariableList **)NULL)
  1303.         return;
  1304.  
  1305. /* Error if read-only */
  1306.  
  1307.     vp = *vpp;
  1308.  
  1309.     if (vp->status & (STATUS_READONLY | STATUS_CANNOT_UNSET))
  1310.     {
  1311.         PrintWarningMessage ("unset: %s %s", vp->name,
  1312.                  (vp->status & STATUS_CANNOT_UNSET)
  1313.                     ? "cannot be unset" : LIT_IsReadonly);
  1314.         return;
  1315.     }
  1316.  
  1317. /* Disable signals */
  1318.  
  1319.     save_signal = signal (SIGINT, SIG_IGN);
  1320.  
  1321. /* Delete it */
  1322.  
  1323.     dummy.index = vp->index;
  1324.     tdelete (&dummy, &VariableTree, SearchVariable);
  1325.     ReleaseMemoryCell ((void *)vp->name);
  1326.  
  1327.     if (vp->value != null)
  1328.         ReleaseMemoryCell ((void *)vp->value);
  1329.  
  1330.     ReleaseMemoryCell ((void *)vp);
  1331.  
  1332. /* Restore signals */
  1333.  
  1334.     signal (SIGINT, save_signal);
  1335.     }
  1336. }
  1337.  
  1338. /*
  1339.  * TFIND - Search the VARIABLE TREE for an entry to delete
  1340.  */
  1341.  
  1342. static int DeleteAllVariables (const void *key1, const void *key2)
  1343. {
  1344.     int            diff;
  1345.  
  1346.     if ((diff = strcmp (((VariableList *)key1)->name,
  1347.             ((VariableList *)key2)->name)) != 0)
  1348.     return diff;
  1349.  
  1350.     if (((VariableList *)key1)->index == -1)
  1351.         return 0;
  1352.  
  1353.     return ((VariableList *)key1)->index - ((VariableList *)key2)->index;
  1354. }
  1355.  
  1356. /*
  1357.  * Execute a test: test <arguments>
  1358.  */
  1359.  
  1360. static int dotest (int argc, char **argv)
  1361. {
  1362.     int        st = 0;
  1363.     char    *End;
  1364.  
  1365.     NewTestProgram = (bool)(strcmp (TestProgram = *argv, LIT_Test) == 0);
  1366.  
  1367. /* Note that -a and -o change meaning if [[ ... ]] is used */
  1368.  
  1369.     if (NewTestProgram)
  1370.     {
  1371.     End = "]]";
  1372.     ListOfTestOperators[0].OperatorID = FILE_EXISTS;
  1373.     ListOfTestOperators[0].OperatorType = UNARY_OP;
  1374.     ListOfTestOperators[1].OperatorID = TEST_OPTION;
  1375.     ListOfTestOperators[1].OperatorType = UNARY_OP;
  1376.     }
  1377.  
  1378.     else
  1379.     {
  1380.     End = "]";
  1381.     ListOfTestOperators[0].OperatorID = BINARY_AND;
  1382.     ListOfTestOperators[0].OperatorType = B_BINARY_OP;
  1383.     ListOfTestOperators[1].OperatorID = BINARY_OR;
  1384.     ListOfTestOperators[1].OperatorType = B_BINARY_OP;
  1385.     }
  1386.  
  1387. /* Check out */
  1388.  
  1389.     CurrentTestOperator = (struct TestOperator *)NULL;
  1390.  
  1391. /* If [ <arguments> ] or [[ <arguments> ]] form, check for end ] or ]] and
  1392.  * remove it
  1393.  */
  1394.  
  1395.     if (NewTestProgram || (strcmp (*argv, "[") == 0))
  1396.     {
  1397.     while (argv[++st] != NOWORD)
  1398.         ;
  1399.  
  1400.     if (strcmp (argv[--st], End) != 0)
  1401.         return PrintWarningMessage ("%s: missing '%s'", TestProgram, End);
  1402.  
  1403.     else
  1404.         argv[st] = NOWORD;
  1405.     }
  1406.  
  1407. /* Check for null expression */
  1408.  
  1409.     if (*(TestArgumentList = &argv[1]) == NOWORD)
  1410.     return 1;
  1411.  
  1412. /* Set abort address */
  1413.  
  1414.     if (setjmp (TestErrorReturn))
  1415.     return 1;
  1416.  
  1417.     st = !TestProcessNextExpression (TestLexicalAnalysis ());
  1418.  
  1419.     if (*(TestArgumentList + 1) != NOWORD)
  1420.     TestSyntaxError ();
  1421.  
  1422.     return (st);
  1423. }
  1424.  
  1425. /*
  1426.  * Process next test expression
  1427.  */
  1428.  
  1429. static int F_LOCAL TestProcessNextExpression (int n)
  1430. {
  1431.     int        res;
  1432.  
  1433.     if (n == END_OF_INPUT)
  1434.     TestSyntaxError ();
  1435.  
  1436.     res = TestANDExpression (n);
  1437.  
  1438.     TestArgumentList++;
  1439.  
  1440.     if (TestLexicalAnalysis () == BINARY_OR)
  1441.     {
  1442.     TestArgumentList++;
  1443.  
  1444.     return TestProcessNextExpression (TestLexicalAnalysis ()) || res;
  1445.     }
  1446.  
  1447.     TestArgumentList--;
  1448.     return res;
  1449. }
  1450.  
  1451. /*
  1452.  * Binary expression ( a AND b)
  1453.  */
  1454.  
  1455. static int F_LOCAL TestANDExpression (int n)
  1456. {
  1457.     int res;
  1458.  
  1459.     if (n == END_OF_INPUT)
  1460.     TestSyntaxError ();
  1461.  
  1462.     res = TestPrimaryExpression (n);
  1463.     TestArgumentList++;
  1464.  
  1465.     if (TestLexicalAnalysis () == BINARY_AND)
  1466.     {
  1467.     TestArgumentList++;
  1468.     return TestANDExpression (TestLexicalAnalysis ()) && res;
  1469.     }
  1470.  
  1471.     TestArgumentList--;
  1472.     return res;
  1473. }
  1474.  
  1475. /*
  1476.  * Handle Primary expression
  1477.  */
  1478.  
  1479. static int F_LOCAL TestPrimaryExpression (int n)
  1480. {
  1481.     int            res;
  1482.  
  1483.     switch (n)
  1484.     {
  1485.     case END_OF_INPUT:
  1486.         TestSyntaxError ();
  1487.  
  1488.         case UNARY_NOT:
  1489.         TestArgumentList++;
  1490.         return !TestPrimaryExpression (TestLexicalAnalysis ());
  1491.  
  1492.     case LPAREN:
  1493.         TestArgumentList++;
  1494.         res = TestProcessNextExpression (TestLexicalAnalysis ());
  1495.  
  1496.         TestArgumentList++;
  1497.         if (TestLexicalAnalysis () != RPAREN)
  1498.         TestSyntaxError ();
  1499.  
  1500.         return res;
  1501.  
  1502. /* Operand */
  1503.  
  1504.     case OPERAND:
  1505.         return TestBinaryOperand ();
  1506.  
  1507. /* unary expression */
  1508.  
  1509.     default:
  1510.         if ((CurrentTestOperator->OperatorType != UNARY_OP) ||
  1511.         (*++TestArgumentList == 0))
  1512.         TestSyntaxError ();
  1513.  
  1514.         return TestUnaryOperand (n);
  1515.     }
  1516. }
  1517.  
  1518. /*
  1519.  * Handle a Binary Operand
  1520.  */
  1521.  
  1522. static int F_LOCAL TestBinaryOperand (void)
  1523. {
  1524.     char        *opnd1, *opnd2;
  1525.     struct stat        s, s1;
  1526.     short        op;
  1527.  
  1528.     opnd1 = *(TestArgumentList++);
  1529.     (void) TestLexicalAnalysis ();
  1530.  
  1531.     if ((CurrentTestOperator != (struct TestOperator *)NULL) &&
  1532.     (CurrentTestOperator->OperatorType == BINARY_OP))
  1533.     {
  1534.     op = CurrentTestOperator->OperatorID;
  1535.  
  1536.     TestArgumentList++;
  1537.  
  1538.     if ((opnd2 = *TestArgumentList) == NOWORD)
  1539.         TestSyntaxError ();
  1540.  
  1541.     switch (op)
  1542.     {
  1543.  
  1544. /* String lengths */
  1545.  
  1546.         case STRING_EQUAL:
  1547.         return strcmp (opnd1, opnd2) == 0;
  1548.  
  1549.         case STRING_NOTEQUAL:
  1550.         return strcmp (opnd1, opnd2) != 0;
  1551.  
  1552.         case STRING_LESS:
  1553.         return strcmp (opnd1, opnd2) < 0;
  1554.  
  1555.         case STRING_GREATER:
  1556.         return strcmp (opnd1, opnd2) > 0;
  1557.  
  1558. /* Numeric comparisions */
  1559.  
  1560.         case NUMBER_EQUAL:
  1561.         return GetNumberForTest (opnd1) == GetNumberForTest (opnd2);
  1562.  
  1563.         case NUMBER_NOTEQUAL:
  1564.         return GetNumberForTest (opnd1) != GetNumberForTest (opnd2);
  1565.  
  1566.         case NUMBER_EQ_GREAT:
  1567.         return GetNumberForTest (opnd1) >= GetNumberForTest (opnd2);
  1568.  
  1569.         case NUMBER_GREATER:
  1570.         return GetNumberForTest (opnd1) > GetNumberForTest (opnd2);
  1571.  
  1572.         case NUMBER_EQ_LESS:
  1573.         return GetNumberForTest (opnd1) <= GetNumberForTest (opnd2);
  1574.  
  1575.         case NUMBER_LESS:
  1576.         return GetNumberForTest (opnd1) < GetNumberForTest (opnd2);
  1577.  
  1578. /* Older and Newer - if file not found - set to current time */
  1579.  
  1580.         case FILE_NEWER:
  1581.         case FILE_OLDER:
  1582.         if (!S_stat (opnd1, &s))
  1583.             return 0;
  1584.  
  1585.         if (!S_stat (opnd2, &s1))
  1586.             s1.st_mtime = 0L;
  1587.  
  1588.         return (op == FILE_NEWER) ? (s.st_mtime > s1.st_mtime)
  1589.                       : (s.st_mtime < s1.st_mtime);
  1590.  
  1591. /*
  1592.  * Equals - difficult on DOS.  So just do want UNIX says, but first compare
  1593.  * the absolute path names
  1594.  */
  1595.  
  1596.         case FILE_EQUAL:
  1597.         {
  1598. #if (OS_TYPE != OS_UNIX)
  1599.         char    *a_opnd1;
  1600.         char    *a_opnd2;
  1601.         int    res;
  1602. #endif
  1603.  
  1604.         if ((!S_stat (opnd1, &s)) || (!S_stat (opnd2, &s1)))
  1605.             return 0;
  1606.  
  1607. #if (OS_TYPE == OS_UNIX)
  1608.         return ((s.st_dev == s1.st_dev) && (s.st_ino == s1.st_ino));
  1609. #else
  1610.  
  1611.         a_opnd1 = AllocateMemoryCell (FFNAME_MAX);
  1612.         a_opnd2 = AllocateMemoryCell (FFNAME_MAX);
  1613.  
  1614.         if ((a_opnd1 == (char *)NULL) || (a_opnd1 == (char *)NULL))
  1615.         {
  1616.             doOutofMemory (LIT_Test);
  1617.             return 0;
  1618.         }
  1619.  
  1620.         GenerateFullExecutablePath (strcpy (a_opnd1, opnd1));
  1621.         GenerateFullExecutablePath (strcpy (a_opnd2, opnd2));
  1622.  
  1623. /* If this is OS/2, we need to decide if to check for HPFS */
  1624.  
  1625. #  if (OS_TYPE != OS_DOS)
  1626.         res = ((!IsHPFSFileSystem (a_opnd1)) ||
  1627.                (ShellGlobalFlags & FLAGS_NOCASE))
  1628.                 ? stricmp (a_opnd1, a_opnd2)
  1629.                 : strcmp (a_opnd1, a_opnd2);
  1630. #  else
  1631.         res = stricmp (a_opnd1, a_opnd2);
  1632. #  endif
  1633.  
  1634.         ReleaseMemoryCell (a_opnd1);
  1635.         ReleaseMemoryCell (a_opnd2);
  1636.  
  1637.         return (res == 0) ? 1
  1638.                   : ((s.st_dev == s1.st_dev) &&
  1639.                      (s.st_ino == s1.st_ino));
  1640. #endif
  1641.         }
  1642.     }
  1643.     }
  1644.  
  1645.     TestArgumentList--;
  1646.     return strlen (opnd1) != 0;
  1647. }
  1648.  
  1649. /*
  1650.  * Handle a Unary Operand
  1651.  */
  1652.  
  1653. static int F_LOCAL TestUnaryOperand (int n)
  1654. {
  1655.     switch (n)
  1656.     {
  1657.     case STRING_ZERO:
  1658.         return strlen (*TestArgumentList) == 0;
  1659.  
  1660.     case STRING_NONZERO:
  1661.         return strlen (*TestArgumentList) != 0;
  1662.  
  1663.     case TEST_OPTION:
  1664.         return TestOptionValue (*TestArgumentList, TRUE) != 0;
  1665.  
  1666. /* File functions */
  1667.  
  1668.     case FILE_EXISTS:
  1669.         return CheckFAccess (*TestArgumentList, F_OK);
  1670.  
  1671.     case FILE_READABLE:
  1672.         return CheckFAccess (*TestArgumentList, R_OK);
  1673.  
  1674.     case FILE_WRITABLE:
  1675.         return CheckFAccess (*TestArgumentList, W_OK);
  1676.  
  1677.     case FILE_EXECUTABLE:
  1678.         return CheckFAccess (*TestArgumentList, X_OK);
  1679.  
  1680.     case FILE_REGULAR:
  1681.         return CheckFType (*TestArgumentList, S_IFREG);
  1682.  
  1683.     case FILE_DIRECTRY:
  1684.         return CheckFType (*TestArgumentList, S_IFDIR);
  1685.  
  1686.     case FILE_NONZERO:
  1687.         return CheckFSize (*TestArgumentList);
  1688.  
  1689.     case FILE_TERMINAL:
  1690.         return IS_TTY ((int)GetNumberForTest (*TestArgumentList));
  1691.  
  1692. /* The following have no meaning on MSDOS or OS/2.  So we always return
  1693.  * fail for compatability
  1694.  */
  1695.  
  1696. #if (OS_TYPE == OS_UNIX)
  1697.     case FILE_USER:
  1698.         return CheckFMode (*TestArgumentList, S_ISUID);
  1699.  
  1700.     case FILE_GROUP:
  1701.         return CheckFMode (*TestArgumentList, S_ISGID);
  1702.  
  1703.     case FILE_TEXT:
  1704.         return CheckFMode (*TestArgumentList, S_ISVTX);
  1705. #else
  1706.     case FILE_USER:
  1707.         return CheckFMode (*TestArgumentList, OS_FILE_HIDDEN);
  1708.  
  1709.     case FILE_GROUP:
  1710.         return CheckFMode (*TestArgumentList, OS_FILE_SYSTEM);
  1711.  
  1712.     case FILE_TEXT:
  1713.         return CheckFMode (*TestArgumentList, OS_FILE_ARCHIVED);
  1714. #endif
  1715.  
  1716.     case FILE_BLOCK:
  1717.         return CheckFType (*TestArgumentList, S_IFBLK);
  1718.  
  1719.     case FILE_CHARACTER:
  1720.         return CheckFType (*TestArgumentList, S_IFCHR);
  1721.  
  1722. #if (OS_TYPE == OS_UNIX)
  1723.     case FILE_FIFO:
  1724.         return CheckFType (*TestArgumentList, S_IFIFO);
  1725.  
  1726.     case FILE_SYMBOLIC:
  1727.         return CheckFType (*TestArgumentList, S_IFLNK);
  1728.  
  1729.     case FILE_SOCKET:
  1730.         return CheckFType (*TestArgumentList, S_IFSOCK);
  1731. #else
  1732.     case FILE_FIFO:
  1733.     case FILE_SYMBOLIC:
  1734.     case FILE_SOCKET:
  1735.         return 0;
  1736. #endif
  1737.  
  1738. /* Under MSDOS and OS/2, we always own the file.  Not necessarily true on
  1739.  * networked versions.  But there is no common way of finding out
  1740.  */
  1741.  
  1742. #if (OS_TYPE == OS_UNIX)
  1743.     case FILE_OWNER:
  1744.         return CheckFOwner (*TestArgumentList);
  1745.  
  1746.     case FILE_GROUPER:
  1747.         return CheckFGroup (*TestArgumentList);
  1748. #else
  1749.     case FILE_OWNER:
  1750.     case FILE_GROUPER:
  1751.         return 1;
  1752. #endif
  1753.     }
  1754.  
  1755.     return 0;
  1756. }
  1757.  
  1758. /* Operator or Operand ? */
  1759.  
  1760. static int F_LOCAL TestLexicalAnalysis (void)
  1761. {
  1762.     struct TestOperator        *op;
  1763.     char            *s = *TestArgumentList;
  1764.  
  1765.     if (s == NOWORD)
  1766.     return END_OF_INPUT;
  1767.  
  1768. /* This is a real pain.  If the current string is a unary operator and the
  1769.  * next string is a binary operator, assume the current string is a parameter
  1770.  * to the binary operator and not a unary operator.
  1771.  */
  1772.  
  1773.     if ((CurrentTestOperator = TestLookupOperator (s))
  1774.                  != (struct TestOperator *)NULL)
  1775.     {
  1776.     s = *(TestArgumentList + 1);
  1777.  
  1778.     if ((CurrentTestOperator->OperatorType != UNARY_OP) ||
  1779.         (s == (char *)NULL) ||
  1780.         ((op = TestLookupOperator (s)) == (struct TestOperator *)NULL) ||
  1781.         (op->OperatorType != BINARY_OP))
  1782.         return CurrentTestOperator->OperatorID;
  1783.     }
  1784.  
  1785.     CurrentTestOperator = (struct TestOperator *)NULL;
  1786.     return OPERAND;
  1787. }
  1788.  
  1789. /*
  1790.  * Look up the string and test for operator
  1791.  */
  1792.  
  1793. static struct TestOperator * F_LOCAL TestLookupOperator (char *s)
  1794. {
  1795.     struct TestOperator        *op = ListOfTestOperators;
  1796.  
  1797.     while (op->OperatorName)
  1798.     {
  1799.     if (strcmp (s, op->OperatorName) == 0)
  1800.         return op;
  1801.  
  1802.     op++;
  1803.     }
  1804.  
  1805.     return (struct TestOperator *)NULL;
  1806. }
  1807.  
  1808. /*
  1809.  * Get a long numeric value
  1810.  */
  1811.  
  1812. static long F_LOCAL GetNumberForTest (char *s)
  1813. {
  1814.     long    l;
  1815.  
  1816.     if (!ConvertNumericValue (s, &l, 10))
  1817.     TestSyntaxError ();
  1818.  
  1819.     return l;
  1820. }
  1821.  
  1822. /*
  1823.  * test syntax error - abort
  1824.  */
  1825.  
  1826. static void F_LOCAL TestSyntaxError (void)
  1827. {
  1828.     PrintWarningMessage (BasicErrorMessage, TestProgram, LIT_SyntaxError);
  1829.     longjmp (TestErrorReturn, 1);
  1830. }
  1831.  
  1832. /*
  1833.  * Select a new drive: x:
  1834.  *
  1835.  * Select the drive, get the current directory and check that we have
  1836.  * actually selected the drive
  1837.  */
  1838.  
  1839. static int dodrive (int argc, char **argv)
  1840. {
  1841.     unsigned int    ndrive = GetDriveNumber (**argv);
  1842.     char        ldir[PATH_MAX + 6];
  1843.     bool        bad = FALSE;
  1844.  
  1845.     if (argc != 1)
  1846.     return UsageError ("drive:");
  1847.  
  1848.     if (SetCurrentDrive (ndrive) == -1)
  1849.     bad = TRUE;
  1850.  
  1851.     else if (!S_getcwd (ldir, ndrive))
  1852.     bad = TRUE;
  1853.     
  1854.     else
  1855.     {
  1856.     GetCurrentDirectoryPath ();
  1857.  
  1858.     if (ndrive != GetCurrentDrive ())
  1859.         bad = TRUE;
  1860.     }
  1861.  
  1862.     if (bad)
  1863.     return PrintWarningMessage (BadDrive, **argv);
  1864.  
  1865.     else
  1866.     return 0;
  1867. }
  1868.  
  1869. /*
  1870.  * Check for -L or -P switch
  1871.  */
  1872.  
  1873. static bool F_LOCAL CheckPhysLogical (char *string, bool *physical)
  1874. {
  1875.     if (strcmp (string, "-L") == 0)
  1876.     {
  1877.     *physical = FALSE;
  1878.     return TRUE;
  1879.     }
  1880.  
  1881.     if (strcmp (string, "-P") == 0)
  1882.     {
  1883.     *physical = TRUE;
  1884.     return TRUE;
  1885.     }
  1886.  
  1887.     return FALSE;
  1888. }
  1889.  
  1890. /*
  1891.  * Select a new directory:
  1892.  *
  1893.  * cd [ directory ]            Select new directory
  1894.  * cd -                 Select previous directory
  1895.  * cd <search string> <new string>    Select directory based on current
  1896.  * cd                    Select Home directory
  1897.  * cd [-L | -P]                Use Logical or physical mapping
  1898.  *                    sort of symbolic links
  1899.  */
  1900.  
  1901. static int dochdir (int argc, char **argv)
  1902. {
  1903.     char        *NewDirectory;    /* Original new directory    */
  1904.     char        *CNDirectory;    /* New directory        */
  1905.     char        *cp;        /* In CDPATH Pointer        */
  1906.     char        *directory;
  1907.     bool        first = TRUE;
  1908.     unsigned int    Length;
  1909.     unsigned int    cdrive;
  1910.     bool        Physical = FALSE;
  1911.     int            Start = 1;
  1912.     int            dcount;
  1913.  
  1914. /* If restricted shell - illegal */
  1915.  
  1916.     if (CheckForRestrictedShell ("cd"))
  1917.     return 1;
  1918.  
  1919.     if ((argc > 1) && CheckPhysLogical (argv[1], &Physical))
  1920.     Start = 2;
  1921.  
  1922.     if (argc - Start > 2)
  1923.     return UsageError ("cd [ -P | -L ] [ directory | - | search replace ]");
  1924.  
  1925. /* Use default ? */
  1926.  
  1927.     if (((NewDirectory = argv[Start]) == NOWORD) &&
  1928.     ((NewDirectory = GetVariableAsString (HomeVariableName,
  1929.                           FALSE)) == null))
  1930.     return PrintWarningMessage ("cd: no home directory");
  1931.  
  1932.     if ((directory = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
  1933.     return doOutofMemory ("cd");
  1934.  
  1935.     if ((strcmp (NewDirectory, ShellOptionsVariable) == 0) &&
  1936.     ((NewDirectory = GetVariableAsString (OldPWDVariable, FALSE)) == null))
  1937.     return PrintWarningMessage ("cd: no previous directory");
  1938.  
  1939. /* Check for substitue */
  1940.  
  1941.     if ((argv[Start] != NOWORD) && (argv[Start + 1] != NOWORD))
  1942.     {
  1943.     if ((cp = strstr (CurrentDirectory->value, argv[Start]))
  1944.         == (char *)NULL)
  1945.         return PrintWarningMessage ("cd: string not in pwd: %s",
  1946.                     argv[Start]);
  1947.  
  1948.     if (strlen (CurrentDirectory->value) - strlen (argv[Start]) +
  1949.         strlen (argv[Start + 1]) >= (size_t)FFNAME_MAX)
  1950.         return PrintWarningMessage ("cd: new directory string too long: %s",
  1951.                     argv[Start]);
  1952. /* Do the substitution */
  1953.  
  1954.     Length = cp - CurrentDirectory->value;
  1955.     strncpy (NewDirectory, CurrentDirectory->value, Length);
  1956.     strcpy (NewDirectory + Length, argv[Start + 1]);
  1957.     strcat (NewDirectory,
  1958.         CurrentDirectory->value + strlen (argv[Start]) + Length);
  1959.     }
  1960.  
  1961. /* Save the current drive */
  1962.  
  1963.     cdrive = GetCurrentDrive ();
  1964.  
  1965. /* Remove trailing / */
  1966.  
  1967.     Length = strlen (NewDirectory) - 1;
  1968.  
  1969.     if (IsPathCharacter (NewDirectory[Length]) &&
  1970.     (!((!Length) || ((Length == 2) && IsDriveCharacter (NewDirectory[1])))))
  1971.     NewDirectory[Length] = 0;
  1972.  
  1973. /*
  1974.  * Special case for . and .., it seems
  1975.  */
  1976.  
  1977.     if ((strcmp (NewDirectory, CurrentDirLiteral) == 0) &&
  1978.     S_chdir (NewDirectory))
  1979.     {
  1980.     GetCurrentDirectoryPath ();
  1981.     return  0;
  1982.     }
  1983.  
  1984.     if (strcmp (NewDirectory, ParentDirLiteral) == 0)
  1985.     {
  1986.     if (S_chdir (NewDirectory))
  1987.     {
  1988.         GetCurrentDirectoryPath ();
  1989.         return  0;
  1990.     }
  1991.  
  1992. /* If we are in the root directory, .. does not move you! */
  1993.  
  1994.     dcount = 0;
  1995.     cp = CurrentDirectory->value;
  1996.  
  1997.     while ((cp = strchr (cp, CHAR_UNIX_DIRECTORY)) != (char *)NULL)
  1998.     {
  1999.         cp++;
  2000.  
  2001.         if (++dcount == 2)
  2002.             break;
  2003.     }
  2004.  
  2005.     if (dcount == 1)
  2006.     {
  2007.         GetCurrentDirectoryPath ();
  2008.         return  0;
  2009.     }
  2010.     }
  2011.  
  2012.  
  2013. /* Scan for the directory.  If there is not a / or : at start, use the
  2014.  * CDPATH variable
  2015.  */
  2016.  
  2017.     cp = (IsPathCharacter (*NewDirectory) ||
  2018.           IsDriveCharacter (*(NewDirectory + 1)))
  2019.         ? null : GetVariableAsString (CDPathLiteral, FALSE);
  2020.  
  2021.     do
  2022.     {
  2023.     cp = BuildNextFullPathName (cp, NewDirectory, directory);
  2024.  
  2025. /* Check for new disk drive */
  2026.  
  2027.     if (Physical && (GetPhysicalPath (directory, FALSE) == (char *)NULL))
  2028.         return doOutofMemory ("cd");
  2029.  
  2030.     CNDirectory = directory;
  2031.  
  2032. /* Was the change successful? */
  2033.  
  2034.     if (GotoDirectory (CNDirectory, cdrive))
  2035.     {
  2036.  
  2037. /* OK - reset the current directory (in the shell) and display the new
  2038.  * path if appropriate
  2039.  */
  2040.  
  2041.         GetCurrentDirectoryPath ();
  2042.  
  2043.         if (!first)
  2044.         puts (CurrentDirectory->value);
  2045.  
  2046.         return 0;
  2047.     }
  2048.  
  2049.     first = FALSE;
  2050.  
  2051.     } while (cp != (char *)NULL);
  2052.  
  2053. /* Restore our original drive and restore directory info */
  2054.  
  2055.     GetCurrentDirectoryPath ();
  2056.  
  2057.     return PrintWarningMessage (BasicErrorMessage, NewDirectory,
  2058.                 "bad directory");
  2059. }
  2060.  
  2061. /*
  2062.  * Execute a shift command: shift [ n ]
  2063.  */
  2064.  
  2065. static int doshift (int argc, char **argv)
  2066. {
  2067.     int        n;
  2068.     char    *Nvalue = argv[1];
  2069.  
  2070.     if (argc > 2)
  2071.     return UsageError ("shift [ count ]");
  2072.  
  2073.     n = (Nvalue != NOWORD) ? GetNumericValue (Nvalue) : 1;
  2074.  
  2075.     if (n < 0)
  2076.     return PrintWarningMessage (LIT_Emsg, LIT_shift, "bad shift value",
  2077.                     Nvalue);
  2078.  
  2079.     if (ParameterCount < n)
  2080.     return PrintWarningMessage (BasicErrorMessage, LIT_shift,
  2081.                     "nothing to shift");
  2082.  
  2083.     return SetUpNewParameterVariables (ParameterArray, n + 1,
  2084.                        ParameterCount + 1, LIT_shift);
  2085. }
  2086.  
  2087. /*
  2088.  * Execute a umask command: umask [ n ]
  2089.  */
  2090.  
  2091. static int doumask (int argc, char **argv)
  2092. {
  2093.     int        i;
  2094.     char    *cp;
  2095.     long    value;
  2096.  
  2097.     if (argc > 2)
  2098.     return UsageError ("umask [ new mask ]");
  2099.  
  2100.     if ((cp = argv[1]) == NOWORD)
  2101.     {
  2102.     umask ((i = umask (0)));
  2103.     printf ("%o\n", i);
  2104.     }
  2105.  
  2106.     else if (!ConvertNumericValue (cp, &value, 8))
  2107.     return PrintWarningMessage ("umask: bad mask (%s)", cp);
  2108.  
  2109. #if (__OS2__)
  2110.     else if (umask ((int)value) == 0xffff)
  2111.     return PrintWarningMessage ("umask: bad value (%s)", cp);
  2112. #else
  2113.     umask ((int)value);
  2114. #endif
  2115.  
  2116.     return 0;
  2117. }
  2118.  
  2119. /*
  2120.  * Execute an exec command: exec [ arguments ]
  2121.  */
  2122.  
  2123. int    doexec (C_Op *t)
  2124. {
  2125.     int        i = 0;
  2126.     jmp_buf    ex;
  2127.     ErrorPoint    OldERP;
  2128.  
  2129. /* Shift arguments */
  2130.  
  2131.     do
  2132.     {
  2133.     t->args[i] = t->args[i + 1];
  2134.     i++;
  2135.  
  2136.     } while (t->args[i] != NOWORD);
  2137.  
  2138. /* Left the I/O as it is */
  2139.  
  2140.     if (i == 1)
  2141.     return RestoreStandardIO (0, FALSE);
  2142.  
  2143.     ProcessingEXECCommand = TRUE;
  2144.     OldERP = e.ErrorReturnPoint;
  2145.  
  2146. /* Set execute function recursive level to zero */
  2147.  
  2148.     Execute_stack_depth = 0;
  2149.     t->ioact = (IO_Actions **)NULL;
  2150.  
  2151.     if (SetErrorPoint (ex) == 0)
  2152.     ExecuteParseTree (t, NOPIPE, NOPIPE, EXEC_WITHOUT_FORK);
  2153.  
  2154. /* Clear the extended file if an interrupt happened */
  2155.  
  2156.     ClearExtendedLineFile ();
  2157.     e.ErrorReturnPoint = OldERP;
  2158.     ProcessingEXECCommand = FALSE;
  2159.     return 1;
  2160. }
  2161.  
  2162. /*
  2163.  * Execute a script in the current shell: . <filename>
  2164.  */
  2165.  
  2166. static int dodot (int argc, char **argv)
  2167. {
  2168.     int        i;
  2169.     char    *sp;
  2170.     char    *cp;
  2171.     char    *l_path;
  2172.     Source    *s;
  2173.     FILE    *Fp;
  2174.  
  2175.     if ((cp = argv[1]) == NOWORD)
  2176.     return 0;
  2177.  
  2178. /* Get some space */
  2179.  
  2180.     if ((l_path = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
  2181.     return doOutofMemory (".");
  2182.  
  2183. /* Save the current drive */
  2184.  
  2185.     sp = ((FindPathCharacter (cp) != (char *)NULL) || IsDriveCharacter (*(cp + 1)))
  2186.       ? null
  2187.       : GetVariableAsString (PathLiteral, FALSE);
  2188.  
  2189.     do
  2190.     {
  2191.     sp = BuildNextFullPathName (sp, cp, l_path);
  2192.  
  2193.     if ((i = OpenForExecution (l_path, (char **)NULL, (int *)NULL)) >= 0)
  2194.     {
  2195.         if ((Fp = ReOpenFile (ReMapIOHandler (i),
  2196.                       sOpenReadMode)) == (FILE *)NULL)
  2197.         return PrintWarningMessage ("Cannot remap file");
  2198.  
  2199.         (s = pushs (SFILE))->u.file = Fp;
  2200.         s->file = cp;
  2201.  
  2202.         RunACommand (s, &argv[1]);
  2203.         S_fclose (Fp, TRUE);
  2204.  
  2205.         return (int)GetVariableAsNumeric (StatusVariable);
  2206.     }
  2207.  
  2208.     } while (sp != (char *)NULL);
  2209.  
  2210.     return PrintWarningMessage (BasicErrorMessage, cp, NotFound);
  2211. }
  2212.  
  2213. /*
  2214.  * Read from standard input into a variable list
  2215.  *
  2216.  * read [-prs] [-u unit] [ variable list ]
  2217.  */
  2218.  
  2219. static int doread (int argc, char **argv)
  2220. {
  2221.     char    *cp, *op;
  2222.     int        i;
  2223.     int        Unit = STDIN_FILENO;
  2224.     bool    EndOfInputDetected = FALSE;
  2225.     int        PreviousCharacter = 0;
  2226.     bool    SaveMode = FALSE;
  2227.     bool    RawMode = FALSE;
  2228.     char    *Prompt = (char *)NULL;
  2229.     char    *NewBuffer;
  2230.     int        eofc;
  2231.  
  2232.     if ((NewBuffer = AllocateMemoryCell (LINE_MAX + 2)) == (char *)NULL)
  2233.     return doOutofMemory (LIT_read);
  2234.  
  2235. /* Check for variable name.  If not defined, use $REPLY */
  2236.  
  2237.     ResetGetOptions ();            /* Reset GetOptions        */
  2238.  
  2239.     while ((i = GetOptions (argc, argv, "prsu:", 0)) != EOF)
  2240.     {
  2241.     switch (i)
  2242.     {
  2243.         case 'p':            /* Clean up process        */
  2244.         break;
  2245.  
  2246.         case 'r':            /* Raw Mode            */
  2247.         RawMode = TRUE;
  2248.         break;
  2249.  
  2250.         case 's':            /* Save a command        */
  2251.         SaveMode = TRUE;
  2252.         break;
  2253.  
  2254.         case 'u':            /* Specify input unit        */
  2255.         if ((Unit = GetUnitNumber (LIT_read)) == -1)
  2256.             return 2;
  2257.  
  2258.         default:
  2259.         return UsageError ("read [ -prs ] [ -u unit ] [ name?prompt ] [ name ... ]");
  2260.     }
  2261.     }
  2262.  
  2263.     argv += OptionIndex;
  2264.     argc -= OptionIndex;
  2265.  
  2266.     if (!argc)
  2267.     argv = Reply_Array;
  2268.  
  2269. /* Get the prompt and write it */
  2270.  
  2271.     LastUserPrompt = null;
  2272.  
  2273.     if ((Prompt = strchr (argv[0], '?')) != (char *)NULL)
  2274.     {
  2275.     *(Prompt++) = 0;
  2276.     LastUserPrompt1 = Prompt;
  2277.     LastUserPrompt = (char *)NULL;
  2278.     }
  2279.  
  2280. /* Check we have a name */
  2281.  
  2282.     if (!strlen (argv[0]))
  2283.     argv = Reply_Array;
  2284.  
  2285. /* Check for valid names */
  2286.  
  2287.     for (i = 0; argv[i] != (char *)NULL; i++)
  2288.     {
  2289.     if (IsValidVariableName (argv[i]))
  2290.     {
  2291.         PrintErrorMessage (BasicErrorMessage, argv[i], LIT_BadID);
  2292.         return 1;
  2293.     }
  2294.     }
  2295.  
  2296. /* Clear the history buffer */
  2297.  
  2298.     FlushHistoryBuffer ();
  2299.  
  2300. /* Read the line from the device */
  2301.  
  2302.     if (ReadALine (Unit, Prompt, SaveMode, FALSE, &eofc))
  2303.     return 1;
  2304.  
  2305.     cp = ConsoleLineBuffer;
  2306.  
  2307. /* For each variable, read the data until a white space is detected */
  2308.  
  2309.     while (*argv != NOWORD)
  2310.     {
  2311.  
  2312. /* Read in until end of line, file or a field separator is detected */
  2313.  
  2314.     op = (char *)NULL;
  2315.  
  2316.     while (!EndOfInputDetected && *cp)
  2317.     {
  2318.  
  2319. /* End of file */
  2320.  
  2321.         if (*cp == (char)eofc)
  2322.         return 1;
  2323.  
  2324. /* Check for Newline or IFS character */
  2325.  
  2326.         if ((*cp == CHAR_NEW_LINE) ||
  2327.         ((argv[1] != NOWORD) &&
  2328.          strchr (GetVariableAsString (IFS, FALSE),
  2329.              *cp) != (char *)NULL))
  2330.         {
  2331.         if (*cp != CHAR_NEW_LINE)
  2332.              ;
  2333.  
  2334.         else if ((PreviousCharacter != CHAR_META) || (RawMode))
  2335.             EndOfInputDetected = TRUE;
  2336.  
  2337. /* Handle continuation line */
  2338.  
  2339.         else if (ReadALine (Unit, Prompt, SaveMode, TRUE, &eofc))
  2340.             return 1;
  2341.  
  2342.         else
  2343.         {
  2344.             cp = ConsoleLineBuffer;
  2345.             PreviousCharacter = 0;
  2346.             continue;
  2347.         }
  2348.  
  2349.         break;
  2350.         }
  2351.  
  2352. /* Save the current character */
  2353.  
  2354.         if (op == (char *)NULL)
  2355.         op = NewBuffer;
  2356.  
  2357.         *(op++) = *cp;
  2358.         PreviousCharacter = *(cp++);
  2359.     }
  2360.  
  2361. /* Skip over terminating character */
  2362.  
  2363.     if (*cp)
  2364.         ++cp;
  2365.  
  2366. /* Check for null string */
  2367.  
  2368.     if (op == (char *)NULL)
  2369.         op = null;
  2370.  
  2371. /* Terminate the string */
  2372.  
  2373.     else
  2374.     {
  2375.         *op = 0;
  2376.  
  2377.         if (!strlen (NewBuffer))
  2378.         continue;
  2379.  
  2380.         else
  2381.         op = NewBuffer;
  2382.     }
  2383.  
  2384. /* Save the string value */
  2385.  
  2386.     SetVariableFromString (*(argv++), op);
  2387.     }
  2388.  
  2389.     ReleaseMemoryCell ((void *)NewBuffer);
  2390.  
  2391.     return 0;
  2392. }
  2393.  
  2394. /*
  2395.  * Read a line from either the console or a file for the read command.
  2396.  * The line is returned in the ConsoleLineBuffer.
  2397.  */
  2398.  
  2399. static bool F_LOCAL ReadALine (int  Unit,    /* Unit to read from    */
  2400.                    char *Prompt,    /* User prompt        */
  2401.                    bool SaveMode,    /* Save in history    */
  2402.                    bool Append,    /* Append to history    */
  2403.                    int *eofc)    /* EOF character    */
  2404. {
  2405.     int        NumberBytesRead;
  2406.     char    *cp;
  2407.     int        x;
  2408.  
  2409. /* Generate the prompt */
  2410.  
  2411.     if ((Prompt != (char *)NULL) && (!IS_Console (Unit)) &&
  2412.     (!IS_TTY (Unit) || (write (Unit, Prompt, strlen (Prompt)) == -1)))
  2413.     feputs (Prompt);
  2414.  
  2415. /* Read the line */
  2416.  
  2417.     *eofc = 0x1a;
  2418.  
  2419.     if (IS_Console (Unit))
  2420.     {
  2421.     *eofc = GetEOFKey ();
  2422.  
  2423.     if (!GetConsoleInput ())        /* get input    */
  2424.         strcpy (ConsoleLineBuffer, LIT_NewLine);
  2425.     }
  2426.  
  2427.     else
  2428.     {
  2429.     NumberBytesRead = 0;
  2430.     cp =  ConsoleLineBuffer;
  2431.  
  2432.     while (NumberBytesRead++ < LINE_MAX)
  2433.     {
  2434.         if ((x = read (Unit, cp, 1)) == -1)
  2435.         return TRUE;
  2436.  
  2437. /* EOF detected as first character on line */
  2438.  
  2439.         if ((NumberBytesRead == 1) && ((!x) || (*cp == (char)*eofc)))
  2440.         return TRUE;
  2441.  
  2442. /* End read if NEWLINE or EOF character detected */
  2443.  
  2444.         if ((!x) || (*cp == CHAR_NEW_LINE) || (*cp == (char)*eofc))
  2445.         break;
  2446.  
  2447.         cp++;
  2448.     }
  2449.  
  2450. /* Terminate the line the same in all cases */
  2451.  
  2452.     *(cp++) = CHAR_NEW_LINE;
  2453.     *cp = 0;
  2454.     }
  2455.  
  2456. /* Save the history.  Clean it up first */
  2457.  
  2458.     if (SaveMode)
  2459.     {
  2460.     char    save;
  2461.  
  2462.     save = CleanUpBuffer (strlen (ConsoleLineBuffer), ConsoleLineBuffer,
  2463.                   *eofc);
  2464.     AddHistory (Append);
  2465.     ConsoleLineBuffer[strlen(ConsoleLineBuffer)] = save;
  2466.     }
  2467.  
  2468.     return FALSE;
  2469. }
  2470.  
  2471. /*
  2472.  * Evaluate an expression: eval <expression>
  2473.  */
  2474.  
  2475. static int doeval (int argc, char **argv)
  2476. {
  2477.     Source    *s;
  2478.  
  2479.     (s = pushs (SWORDS))->u.strv = argv + 1;
  2480.     return RunACommand (s, (char **)NULL);
  2481. }
  2482.  
  2483. /*
  2484.  * Map signals.  
  2485.  *
  2486.  * Numeric values are assumed to be UNIX - map to appropriate DOS signal.
  2487.  * Character values are just mapped to the DOS signal
  2488.  */
  2489.  
  2490. static struct TrapSignalList * F_LOCAL LookupSignalName (char *name)
  2491. {
  2492.     static struct TrapSignalList    Numeric = {NULL, 0};
  2493.     int                n;
  2494.  
  2495.     if (isdigit (*name))
  2496.     {
  2497.     if (((n = GetNumericValue (name)) < 0) || (n >= MAX_SIG_MAP))
  2498.         return (struct TrapSignalList *)NULL;
  2499.  
  2500. #if (OS_TYPE == OS_UNIX)
  2501.     Numeric.signo = n;
  2502. #else
  2503.     Numeric.signo = UnixToDosSignals [n];
  2504. #endif
  2505.     return &Numeric;
  2506.     }
  2507.  
  2508. /* Check the character names */
  2509.  
  2510.     for (n = 0; n < MAX_TRAP_SIGNALS; n++)
  2511.     {
  2512.         if (stricmp (name, TrapSignalList[n].name) == 0)
  2513.         return &TrapSignalList[n];
  2514.     }
  2515.  
  2516.     return (struct TrapSignalList *)NULL;
  2517. }
  2518.  
  2519. /*
  2520.  * Execute a trap: trap [ number... ] [ command ]
  2521.  */
  2522.  
  2523. static int dotrap (int argc, char **argv)
  2524. {
  2525.     int                i;
  2526.     bool            SetSignal;
  2527.     char            tval[10];
  2528.     char            *cp;
  2529.     struct TrapSignalList    *SignalInfo;
  2530.     void            (*NewSignalFunc)(int);
  2531.  
  2532.     if ((argc == 2) && (strcmp (argv[1], "-l") == 0))
  2533.     {
  2534.     for (i = 3; i < MAX_TRAP_SIGNALS; i++)
  2535.         puts (TrapSignalList[i].name);
  2536.  
  2537.     return 0;
  2538.     }
  2539.  
  2540. /* Display active traps ? */
  2541.  
  2542.     if (argc < 2)
  2543.     {
  2544.  
  2545. /* Display trap - look up each trap and print those we find */
  2546.  
  2547.     for (i = 0; i < NSIG; i++)
  2548.     {
  2549.         sprintf (tval, "~%d", i);
  2550.  
  2551.         if ((cp = GetVariableAsString (tval, FALSE)) != null)
  2552.         printf ("%u: %s\n", i, cp);
  2553.     }
  2554.  
  2555.     if ((cp = GetVariableAsString (Trap_DEBUG, FALSE)) != null)
  2556.         printf (BasicErrorMessage, &Trap_DEBUG[1], cp);
  2557.  
  2558.     if ((cp = GetVariableAsString (Trap_ERR, FALSE)) != null)
  2559.         printf (BasicErrorMessage, &Trap_ERR[1], cp);
  2560.  
  2561.     return 0;
  2562.     }
  2563.  
  2564. /* Check to see if signal re-set */
  2565.  
  2566.     SetSignal = C2bool(LookupSignalName (argv[1]) ==
  2567.                 (struct TrapSignalList *)NULL);
  2568.  
  2569.     for (i = SetSignal ? 2 : 1; argv[i] != NOWORD; ++i)
  2570.     {
  2571.     if ((SignalInfo = LookupSignalName (argv[i]))
  2572.             == (struct TrapSignalList *)NULL)
  2573.         return PrintWarningMessage ("trap: bad signal number - %s",
  2574.                         argv[i]);
  2575.  
  2576. /* Check for no UNIX to DOS mapping */
  2577.  
  2578.     if (SignalInfo->signo == SIG_NO_MAP)
  2579.     {
  2580.         if (!FL_TEST (FLAG_WARNING))
  2581.         PrintWarningMessage ("trap: No UNIX to DOS signal map for %s",
  2582.                      argv[i]);
  2583.  
  2584.         continue;
  2585.     }
  2586.  
  2587. /* Check for ERR or DEBUG.  cp points to the variable name */
  2588.  
  2589.     if (SignalInfo->signo == SIG_SPECIAL)
  2590.         cp = (SignalInfo->name) - 1;
  2591.  
  2592. /* Generate the variable name for a numeric value */
  2593.  
  2594.     else
  2595.         sprintf (cp = tval, "~%d", SignalInfo->signo);
  2596.  
  2597. /* Remove the old processing */
  2598.  
  2599.     RemoveVariable (cp, 0);
  2600.  
  2601. /* Default to new function of ignore! */
  2602.  
  2603.     NewSignalFunc = SIG_DFL;
  2604.  
  2605. /* Re-define signal processing */
  2606.  
  2607.     if (SetSignal)
  2608.     {
  2609.         if (*argv[1] != 0)
  2610.         {
  2611.         SetVariableFromString (cp, argv[1]);
  2612.         NewSignalFunc = TerminateSignalled;
  2613.         }
  2614.  
  2615.         else
  2616.         NewSignalFunc = SIG_IGN;
  2617.     }
  2618.  
  2619. /* Clear signal processing */
  2620.  
  2621.     else if (InteractiveFlag)
  2622.     {
  2623.         if (SignalInfo->signo == SIGINT)
  2624.         NewSignalFunc = InterruptSignalled;
  2625.  
  2626. #ifdef SIGQUIT
  2627.         else 
  2628.         NewSignalFunc = (SignalInfo->signo == SIGQUIT)
  2629.                     ? SIG_IGN : SIG_DFL;
  2630. #endif
  2631.     }
  2632.  
  2633. /* Set up new signal function */
  2634.  
  2635.     if (SignalInfo->signo > 0)
  2636.         signal (SignalInfo->signo, NewSignalFunc);
  2637.  
  2638.     }
  2639.  
  2640.     return 0;
  2641. }
  2642.  
  2643. /*
  2644.  * BREAK and CONTINUE processing: break/continue [ n ]
  2645.  */
  2646.  
  2647. static int dobreak (int argc, char **argv)
  2648. {
  2649.     if (argc > 2)
  2650.     return UsageError ("break [ count ]");
  2651.  
  2652.     return BreakContinueProcessing (argv[1], BC_BREAK);
  2653. }
  2654.  
  2655. static int docontinue (int argc, char **argv)
  2656. {
  2657.     if (argc > 2)
  2658.     return UsageError ("continue [ count ]");
  2659.  
  2660.     return BreakContinueProcessing (argv[1], BC_CONTINUE);
  2661. }
  2662.  
  2663. static int F_LOCAL    BreakContinueProcessing (char *NumberOfLevels,
  2664.                          int Type)
  2665. {
  2666.     Break_C    *Break_Loc;
  2667.     int        LevelNumber;
  2668.     char    *cType = (Type == BC_BREAK) ? LIT_break : LIT_continue;
  2669.  
  2670.  
  2671.     LevelNumber = (NumberOfLevels == (char *)NULL)
  2672.             ? 1 : GetNumericValue (NumberOfLevels);
  2673.  
  2674.     if (LevelNumber < 0)
  2675.     return PrintWarningMessage (LIT_Emsg, cType, "bad level number",
  2676.                     NumberOfLevels);
  2677.  
  2678. /* If level is invalid - clear all levels */
  2679.  
  2680.     if (LevelNumber <= 0)
  2681.     LevelNumber = 999;
  2682.  
  2683. /* Move down the stack */
  2684.  
  2685.     do
  2686.     {
  2687.     if ((Break_Loc = Break_List) == (Break_C *)NULL)
  2688.         break;
  2689.  
  2690.     Break_List = Break_Loc->NextExitLevel;
  2691.  
  2692.     } while (--LevelNumber);
  2693.  
  2694. /* Check level */
  2695.  
  2696.     if (LevelNumber)
  2697.     return PrintWarningMessage (BasicErrorMessage, cType, "bad level");
  2698.  
  2699.     longjmp (Break_Loc->CurrentReturnPoint, Type);
  2700.  
  2701. /* NOTREACHED */
  2702.     return 1;
  2703. }
  2704.  
  2705. /*
  2706.  * Exit function: exit [ status ]
  2707.  */
  2708.  
  2709. static int doexit (int argc, char **argv)
  2710. {
  2711.     Break_C    *SShell_Loc = SShell_List;
  2712.  
  2713.     ProcessingEXECCommand = FALSE;
  2714.  
  2715.     if (argc > 2)
  2716.     return UsageError ("exit [ status ]");
  2717.  
  2718. /* Set up error codes */
  2719.  
  2720.     ExitStatus = (argv[1] != NOWORD) ? GetNumericValue (argv[1]) : 0;
  2721.  
  2722.     SetVariableFromNumeric (StatusVariable, (long)abs (ExitStatus));
  2723.  
  2724. /* Are we in a subshell.  Yes - do a longjmp instead of an exit */
  2725.  
  2726.     if (SShell_Loc != (Break_C *)NULL)
  2727.     {
  2728.     SShell_List = SShell_Loc->NextExitLevel;
  2729.     longjmp (SShell_Loc->CurrentReturnPoint, ExitStatus);
  2730.     }
  2731.  
  2732. /*
  2733.  * Check for active jobs
  2734.  */
  2735.  
  2736. #if (OS_TYPE != OS_DOS)
  2737.     if (!ExitWithJobsActive)
  2738.     {
  2739.     if (NumberOfActiveJobs () && InteractiveFlag)
  2740.     {
  2741.         feputs ("You have running jobs.\n");
  2742.         ExitWithJobsActive = TRUE;
  2743.         return 0;
  2744.     }
  2745.     }
  2746. #endif
  2747.  
  2748.     ExitTheShell (FALSE);
  2749.     return ExitStatus;
  2750. }
  2751.  
  2752. /*
  2753.  * Function return: return [ status ]
  2754.  *
  2755.  * Set exit value and return via a long jmp
  2756.  */
  2757.  
  2758. static int doreturn (int argc, char **argv)
  2759. {
  2760.     Break_C    *Return_Loc = Return_List;
  2761.     int        Retval = 0;
  2762.  
  2763.     if ((argc > 2) || 
  2764.     ((argc == 2) && ((Retval = GetNumericValue (argv[1])) == -1)))
  2765.     return UsageError (ReturnUsage);
  2766.  
  2767.     SetVariableFromNumeric (StatusVariable, (long) abs (Retval));
  2768.  
  2769. /* If the return address is defined - return to it.  Otherwise, return
  2770.  * the value
  2771.  */
  2772.  
  2773.     if (Return_Loc != (Break_C *)NULL)
  2774.     {
  2775.     Return_List = Return_Loc->NextExitLevel;
  2776.     longjmp (Return_Loc->CurrentReturnPoint, 1);
  2777.     }
  2778.  
  2779.     return Retval;
  2780. }
  2781.  
  2782. /*
  2783.  * Set function:  set [ -/+flags ] [ parameters... ]
  2784.  */
  2785.  
  2786. static int doset (int argc, char **argv)
  2787. {
  2788.     int            i;
  2789.  
  2790. /* Display ? */
  2791.  
  2792.     if (argc < 2)
  2793.     return ListAllVariables (0xffff, TRUE);
  2794.  
  2795. /* Set/Unset a flag ? */
  2796.  
  2797.     ResetGetOptions ();            /* Reset GetOptions        */
  2798.  
  2799.     while ((i = GetOptions (argc, argv, "VMA:abcdefghijklmno:pqrstuvwxyz",
  2800.                 GETOPT_PLUS | GETOPT_AMISSING)) != EOF)
  2801.     {
  2802.     switch (i)
  2803.     {
  2804.         case '?':            /* Unknown */
  2805.         if (BadOptionValue != 'o')
  2806.             return UsageError ("set [ [-|+][switches] ] [ [-|+]o option ] [ parameter=value ] args");
  2807.         
  2808.         return PrintOptionSettings ();
  2809.  
  2810.         case 'r':
  2811.         return PrintWarningMessage ("set: r switch cannot be changed");
  2812.  
  2813.         case 'o':
  2814.         if ((!ChangeInitialisationValue (OptionArgument,
  2815.                             OptionStart == '-')) &&
  2816.             (!ChangeOptionValue (OptionArgument,
  2817.                      (bool)(OptionStart == '-'))))
  2818.             return PrintWarningMessage ("set: -o bad option (%s)",
  2819.                         OptionArgument);
  2820.  
  2821.         break;
  2822.  
  2823.         case 'A':
  2824.         if (OptionStart == '-')        /* If -, remove all values */
  2825.             RemoveVariable (OptionArgument, -1);
  2826.  
  2827.         i = 0;
  2828.  
  2829.         while (argv[OptionIndex] != NOWORD)
  2830.             SetVariableArrayFromString (OptionArgument, i++,
  2831.                         argv[OptionIndex++]);
  2832.  
  2833.         return 0;
  2834.  
  2835.         case 'M':
  2836.         if (OptionStart == '-')        /* If -, remove all values */
  2837.             ShellGlobalFlags |= FLAGS_MSDOS_FORMAT;
  2838.  
  2839.         else
  2840.             ShellGlobalFlags &= ~FLAGS_MSDOS_FORMAT;
  2841.  
  2842.             break;
  2843.         
  2844.         case 'V':
  2845.         SetVerifyStatus (C2bool (OptionStart == '-'));
  2846.         break;
  2847.  
  2848.         default:
  2849.         if ((i == 'e') && InteractiveFlag)
  2850.             continue;
  2851.  
  2852.         SetClearFlag (i, (bool)(OptionStart == '-'));
  2853.     }
  2854.     }
  2855.  
  2856.     SetShellSwitches ();
  2857.  
  2858. /* Check for --, ++, - and +, which we skip */
  2859.  
  2860.     if ((OptionIndex != argc) &&
  2861.     ((!strcmp (argv[OptionIndex], DoubleHypen))        ||
  2862.      (!strcmp (argv[OptionIndex], ShellOptionsVariable))    ||
  2863.      (!strcmp (argv[OptionIndex], "+"))))
  2864.     OptionIndex++;
  2865.  
  2866. /* Set up parameters ? */
  2867.  
  2868.     if (OptionIndex != argc)
  2869.     {
  2870.     ResetGetoptsValues (TRUE);
  2871.     return SetUpNewParameterVariables (argv, OptionIndex, argc, "set");
  2872.     }
  2873.  
  2874.     else
  2875.     return 0;
  2876. }
  2877.  
  2878.  
  2879. /*
  2880.  * Print the list of functions: functions [ names ]
  2881.  */
  2882.  
  2883. static int dofunctions (int argc, char **argv)
  2884. {
  2885.     FunctionList    *fp;
  2886.     int            i;
  2887.  
  2888.     if (argc < 2)
  2889.     return PrintAllFunctions ();
  2890.  
  2891.     for (i = 1; argv[i] != NOWORD; ++i)
  2892.     {
  2893.     if ((fp = LookUpFunction (argv[i], TRUE)) != (FunctionList *)NULL)
  2894.         PrintFunction (fp->tree, PF_MODE_NORMAL);
  2895.  
  2896.     else
  2897.         PrintWarningMessage ("functions: %s is not a function", argv[i]);
  2898.     }
  2899.  
  2900.     return 0;
  2901. }
  2902.  
  2903. /*
  2904.  * History functions - history [-ieds]
  2905.  */
  2906.  
  2907. static int dohistory (int argc, char **argv)
  2908. {
  2909.     int        i;
  2910.     int        Start;
  2911.     long    value;
  2912.  
  2913.     if (!InteractiveFlag)
  2914.     return 1;
  2915.  
  2916.     if (argc < 2)
  2917.         Start = GetLastHistoryEvent () + 1;
  2918.  
  2919. /*
  2920.  * Check for options
  2921.  */
  2922.  
  2923.     else if (**(argv + 1) == CHAR_SWITCH)
  2924.     {
  2925.     ResetGetOptions ();        /* Reset GetOptions        */
  2926.  
  2927.     while ((i = GetOptions (argc, argv, "sidel", 0)) != EOF)
  2928.     {
  2929.         switch (i)
  2930.         {
  2931.         case 's':
  2932.             DumpHistory ();
  2933.             break;
  2934.  
  2935.         case 'i':
  2936.             ClearHistory ();
  2937.             break;
  2938.  
  2939.         case 'l':
  2940.             LoadHistory ();
  2941.             break;
  2942.  
  2943.         case 'd':
  2944.             HistoryEnabled = FALSE;
  2945.             break;
  2946.  
  2947.         case 'e':
  2948.             HistoryEnabled = TRUE;
  2949.             break;
  2950.  
  2951.         default:
  2952.             return UsageError (HistoryUsage);
  2953.         }
  2954.     }
  2955.  
  2956.     return (OptionIndex != argc) ? UsageError (HistoryUsage) : 0;
  2957.     }
  2958.  
  2959. /* Check for number of display */
  2960.  
  2961.     else if ((argc == 2) && ConvertNumericValue (*(argv + 1), &value, 0))
  2962.     Start = (int)value + 1;
  2963.  
  2964.     else
  2965.     return UsageError (HistoryUsage);
  2966.  
  2967. /*
  2968.  * Display history
  2969.  */
  2970.  
  2971.     if ((i = (GetLastHistoryEvent () + 1) - Start) < 0)
  2972.     i = 0;
  2973.  
  2974.     PrintHistory (FALSE, TRUE, i, GetLastHistoryEvent () + 1, stdout);
  2975.     return 0;
  2976. }
  2977.  
  2978. /*
  2979.  * Type function: type [ command ]
  2980.  *
  2981.  * For each name, indicate how it would be interpreted
  2982.  */
  2983.  
  2984. static int dowhence (int argc, char **argv)
  2985. {
  2986.     char        *cp;
  2987.     int            n;            /* Argument count    */
  2988.     int            inb;            /* Inbuilt function    */
  2989.     bool        v_flag;
  2990.     bool        p_flag = FALSE;
  2991.     bool        t_flag = FALSE;
  2992.     char        *l_path;
  2993.     AliasList        *al;
  2994.  
  2995. /* Get some memory for the buffer */
  2996.  
  2997.     if ((l_path = AllocateMemoryCell (FFNAME_MAX + 4)) == (char *)NULL)
  2998.     return doOutofMemory (*argv);
  2999.  
  3000.     v_flag = (bool)(strcmp (*argv, LIT_type) == 0);
  3001.  
  3002.     ResetGetOptions ();            /* Reset GetOptions        */
  3003.  
  3004.     while ((n = GetOptions (argc, argv, "pvt", 0)) != EOF)
  3005.     {
  3006.     switch (n)
  3007.     {
  3008.         case 'v':    v_flag = TRUE;    break;
  3009.         case 'p':    p_flag = TRUE;    break;
  3010.         case 't':    t_flag = TRUE;    break;
  3011.  
  3012.         default:
  3013.         return UsageError (WhenceUsage);
  3014.     }
  3015.     }
  3016.  
  3017. /* Process each parameter */
  3018.  
  3019.     while ((cp = argv[OptionIndex++]) != NOWORD)
  3020.     {
  3021.  
  3022. /* Check for alias */
  3023.  
  3024.     if ((al = LookUpAlias (cp, FALSE)) != (AliasList *)NULL)
  3025.     {
  3026.         if (v_flag)
  3027.         printf ("%s is %s alias for %s", cp,
  3028.             (al->AFlags & ALIAS_TRACKED) ? "a tracked"
  3029.                              : "an", al->value);
  3030.  
  3031.         else
  3032.         foputs (al->value);
  3033.     }
  3034.  
  3035.  
  3036. /* Check for currently use inbuilt version */
  3037.  
  3038.     else if (!p_flag && IsCommandBuiltIn (cp, &inb) && (inb & BLT_CURRENT))
  3039.         WhenceLocation (v_flag, cp, ShellInternalCommand);
  3040.  
  3041. /* Check for a function */
  3042.  
  3043.     else if (!p_flag &&
  3044.          (LookUpFunction (cp, FALSE) != (FunctionList *)NULL))
  3045.         WhenceLocation (v_flag, cp, "is a function");
  3046.  
  3047. /* Scan the path for an executable */
  3048.  
  3049.     else if (FindLocationOfExecutable (l_path, cp) != EXTENSION_NOT_FOUND)
  3050.     {
  3051.         PATH_TO_LOWER_CASE (l_path);
  3052.  
  3053.         if (v_flag)
  3054.         printf ("%s is ", cp);
  3055.  
  3056.         foputs (PATH_TO_UNIX (l_path));
  3057.  
  3058.         if (t_flag)
  3059.             WhenceType (l_path);
  3060.     }
  3061.  
  3062. /* If not found, check for inbuilt version */
  3063.  
  3064.     else if (!p_flag && (IsCommandBuiltIn (cp, &inb)))
  3065.         WhenceLocation (v_flag, cp, ShellInternalCommand);
  3066.  
  3067.     else if (!p_flag && LookUpSymbol (cp))
  3068.         WhenceLocation (v_flag, cp, "is a shell keyword");
  3069.  
  3070.     else if (v_flag)
  3071.     {
  3072.         PrintWarningMessage (LIT_2Strings, cp, NotFound);
  3073.         continue;
  3074.     }
  3075.  
  3076.     fputchar (CHAR_NEW_LINE);
  3077.     }
  3078.  
  3079.     return 0;
  3080. }
  3081.  
  3082. /*
  3083.  * Output file type
  3084.  */
  3085.  
  3086. static char     *ExeType_Error[] = {
  3087.     "Not known",
  3088.     "Bad image",
  3089.     "Not executable",
  3090.     "File not found"
  3091. };
  3092.  
  3093. static char    *ExeType_Dos[] = {
  3094.     "DOS Character",
  3095.     "Windows 16-bit",
  3096.     "Watcom 32 bit",
  3097.     "OS/2 Bound"
  3098. };
  3099.  
  3100. static char    *ExeType_OS2[] = {
  3101.    "Not PM compatible",
  3102.    "PM compatible",
  3103.    "PM"
  3104. };
  3105.  
  3106. static char    *ExeType_NT[] = {
  3107.     "Native",
  3108.     "Windows GUI",
  3109.     "Windows CUI",
  3110.     "OS2",
  3111.     "POSIX"
  3112. };
  3113.  
  3114. static void F_LOCAL WhenceType (char *path)
  3115. {
  3116.     unsigned long type = QueryApplicationType (path);
  3117.    
  3118.     foputs ("  [");
  3119.  
  3120.     if (type & EXETYPE_ERROR)
  3121.     foputs (ExeType_Error [type - 1]);
  3122.  
  3123.     else if (type & EXETYPE_DOS)
  3124.     foputs (ExeType_Dos [(type >> 4) - 1]);
  3125.  
  3126.     else if (type & EXETYPE_OS2)
  3127.     printf ("OS2 %dbit %s", (type & EXETYPE_OS2_32) ? 32 : 16,
  3128.          ExeType_OS2 [((type & EXETYPE_OS2_TYPE) >> 8) - 1]);
  3129.  
  3130.     else if (type & EXETYPE_NT)
  3131.     printf ("Win NT %s subsystem", ExeType_NT [(type >> 12) - 1]);
  3132.  
  3133.     fputchar (']');
  3134. }
  3135.  
  3136. /*
  3137.  * Output location
  3138.  */
  3139.  
  3140. static void F_LOCAL WhenceLocation (bool v_flag, char *cp, char *mes)
  3141. {
  3142.     foputs (cp);
  3143.  
  3144.     if (v_flag)
  3145.     printf (" %s", mes);
  3146. }
  3147.  
  3148. /*
  3149.  * Table of internal commands.  Note that this table is sort in alphabetic
  3150.  * order.
  3151.  */
  3152.  
  3153. static struct builtin    builtin[] = {
  3154.     { "((",        dolet,        (BLT_ALWAYS | BLT_CURRENT) },
  3155.     { ".",        dodot,        (BLT_ALWAYS | BLT_CURRENT |
  3156.                      BLT_CENVIRON) },
  3157.     { ":",        dolabel,    (BLT_ALWAYS | BLT_CURRENT |
  3158.                      BLT_CENVIRON) },
  3159.     { "[",        dotest,        BLT_CURRENT },
  3160.     { LIT_Test,    dotest,        (BLT_ALWAYS | BLT_CURRENT |
  3161.                      BLT_CENVIRON | BLT_NOGLOB) },
  3162.     { LIT_alias,    doalias,    (BLT_ALWAYS | BLT_CURRENT |
  3163.                      BLT_CENVIRON | BLT_NOGLOB |
  3164.                      BLT_NOWORDS) },
  3165. #if defined (FLAGS_EMACS) || defined (FLAGS_GMACS)
  3166.     { "bind",    dobind,        (BLT_CURRENT | BLT_CENVIRON) },
  3167. #endif
  3168.     { LIT_break,    dobreak,    (BLT_CURRENT | BLT_CENVIRON) },
  3169.     { LIT_builtin,    dobuiltin,    (BLT_ALWAYS | BLT_CURRENT) },
  3170.     { "cd",        dochdir,    BLT_CURRENT },
  3171.     { "chdir",    dochdir,    (BLT_ALWAYS | BLT_CURRENT) },
  3172.     { LIT_continue,    docontinue,    (BLT_CURRENT | BLT_CENVIRON) },
  3173.  
  3174. #if (OS_TYPE != OS_DOS) 
  3175.     { "detach",    dodetach,    (BLT_ALWAYS | BLT_CURRENT) },
  3176. #endif
  3177.  
  3178.     { "echo",    doecho,        BLT_CURRENT },
  3179.     { "eval",    doeval,        (BLT_CURRENT | BLT_CENVIRON) },
  3180.     { LIT_exec,    (int (*)(int, char **)) doexec,
  3181.                     (BLT_CURRENT | BLT_CENVIRON) },
  3182.     { LIT_exit,    doexit,        (BLT_CURRENT | BLT_CENVIRON) },
  3183.     { LIT_export,    doexport,    (BLT_CURRENT | BLT_CENVIRON |
  3184.                      BLT_NOGLOB | BLT_NOWORDS) },
  3185.  
  3186. #if (OS_TYPE == OS_OS2)
  3187.     { "extproc",    dolabel,    (BLT_CURRENT | BLT_CENVIRON) },
  3188. #endif
  3189.  
  3190.     { "false",    dofalse,    BLT_CURRENT },
  3191.     { "fc",        dofc,        BLT_CURRENT },
  3192.     { "functions",    dofunctions,    (BLT_ALWAYS | BLT_CURRENT) },
  3193.     { "getopts",    dogetopts,    BLT_CURRENT },
  3194.     { LIT_history,    dohistory,    BLT_CURRENT },
  3195.  
  3196. #if (OS_TYPE != OS_DOS) 
  3197.     { "jobs",    dojobs,        BLT_CURRENT },
  3198.     { "kill",    dokill,        BLT_CURRENT },
  3199. #endif
  3200.  
  3201.     { "let",    dolet,        BLT_CURRENT },
  3202.     { "msdos",    domsdos,    BLT_CURRENT },
  3203.     { LIT_print,    doecho,        BLT_CURRENT },
  3204.     { "pwd",    dopwd,        BLT_CURRENT },
  3205.     { LIT_read,    doread,        BLT_CURRENT },
  3206.     { "readonly",    doreadonly,    (BLT_CURRENT | BLT_CENVIRON |
  3207.                      BLT_NOGLOB | BLT_NOWORDS) },
  3208.     { "return",    doreturn,    (BLT_CURRENT | BLT_CENVIRON) },
  3209.     { "set",    doset,        BLT_CURRENT },
  3210.     { "shellinfo",    doshellinfo,    BLT_CURRENT },
  3211.     { LIT_shift,    doshift,    (BLT_CURRENT | BLT_CENVIRON) },
  3212.  
  3213. #if (OS_TYPE == OS_OS2)
  3214.     { "start",    dostart,    BLT_CURRENT },
  3215. #endif
  3216.  
  3217.     { "swap",    doswap,        BLT_CURRENT },
  3218.     { "test",    dotest,        BLT_CURRENT },
  3219. #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32) && !defined (__WATCOMC__)
  3220.     { "times",    dotimes,    BLT_CURRENT },
  3221. #endif
  3222. #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_UNIX)
  3223.     { "times",    dotimes,    BLT_CURRENT },
  3224. #endif
  3225.     { "trap",    dotrap,        (BLT_CURRENT | BLT_CENVIRON) },
  3226.     { "true",    dolabel,    BLT_CURRENT },
  3227.     { LIT_type,    dowhence,    BLT_CURRENT },
  3228.     { "typeset",    dotypeset,    (BLT_CURRENT | BLT_CENVIRON |
  3229.                      BLT_NOGLOB | BLT_NOWORDS) },
  3230.     { "umask",    doumask,    BLT_CURRENT },
  3231.     { LIT_unalias,    dounalias,    (BLT_ALWAYS | BLT_CURRENT) },
  3232.     { LIT_unfunction,
  3233.             dounset,    BLT_CURRENT },
  3234.     { "unset",    dounset,    BLT_CURRENT },
  3235.     { "ver",    dover,        BLT_CURRENT },
  3236.  
  3237. #if (OS_TYPE != OS_DOS)
  3238.     { "wait",    dowait,        BLT_CURRENT },
  3239. #endif
  3240.  
  3241.     { "whence",    dowhence,    BLT_CURRENT },
  3242.     { (char *)NULL,    (int (*)(int, char **))NULL,    0 }
  3243. };
  3244.  
  3245. /*
  3246.  * Look up a built in command
  3247.  */
  3248.  
  3249. int (*IsCommandBuiltIn (char *s, int *b))(int, char **)
  3250. {
  3251.     struct builtin    *bp;
  3252.     int            res;
  3253.  
  3254.     *b = 0;
  3255.  
  3256. /* Check for change drive */
  3257.  
  3258.     if ((strlen (s) == 2) && isalpha (*s) && IsDriveCharacter (*(s + 1)))
  3259.     {
  3260.     *b = BLT_ALWAYS | BLT_CURRENT;
  3261.     return dodrive;
  3262.     }
  3263.  
  3264. /* Search for command */
  3265.  
  3266.     for (bp = builtin; bp->command != (char *)NULL; bp++)
  3267.     {
  3268.     if ((res = NOCASE_COMPARE (bp->command, s)) >= 0)
  3269.     {
  3270.         if (res != 0)
  3271.         {
  3272.         if (!isalpha (*(bp->command)))
  3273.             continue;
  3274.  
  3275.             break;
  3276.         }
  3277.  
  3278.         *b = bp->mode;
  3279.         return bp->fn;
  3280.     }
  3281.     }
  3282.  
  3283.     return (int (*)(int, char **))NULL;
  3284. }
  3285.  
  3286. /*
  3287.  * Builtin - either list builtins or execute it
  3288.  *
  3289.  * builtin [-asd] [ names ]
  3290.  */
  3291.  
  3292. static int dobuiltin (int argc, char **argv)
  3293. {
  3294.     struct builtin    *bp;
  3295.     int            (*shcom)(int, char **) = (int (*)(int, char **))NULL;
  3296.     int            ReturnValue = 0;
  3297.     char        *carg;
  3298.     int            mode;
  3299.     int            action = -1;
  3300.     int            i;
  3301.  
  3302.     if (argc < 2)
  3303.     {
  3304.     for (bp = builtin; bp->command != (char *)NULL; bp++)
  3305.         printf (LIT_3Strings, LIT_builtin, bp->command,
  3306.               (bp->mode & BLT_CURRENT) ? " - preferred" : "");
  3307.     return 0;
  3308.     }
  3309.  
  3310. /* Check for changing options */
  3311.  
  3312.     ResetGetOptions ();            /* Reset GetOptions        */
  3313.  
  3314.     while ((i = GetOptions (argc, argv, "sad", 0)) != EOF)
  3315.     {
  3316.     switch (i)
  3317.     {
  3318.         case 's':    action = 0;    break;
  3319.         case 'a':    action = 1;    break;
  3320.         case 'd':    action = 2;    break;
  3321.  
  3322.         default:
  3323.         return UsageError (BuiltinUsage);
  3324.     }
  3325.     }
  3326.  
  3327. /* Check to see if we know about the builtin version */
  3328.  
  3329.     if (action == -1)
  3330.     {
  3331.     if ((shcom = IsCommandBuiltIn (argv[1], &mode))
  3332.            == (int (*)(int, char **))NULL)
  3333.     {
  3334.         printf (BasicErrorMessage, argv[1], NotBuiltinCommand);
  3335.         return 1;
  3336.     }
  3337.  
  3338. /* Yes - so execute the builtin version.  Set up the word list correctly */
  3339.  
  3340.     argv++;
  3341.     ReturnValue = (*shcom)(CountNumberArguments (argv), argv);
  3342.     argv--;
  3343.     return ReturnValue;
  3344.     }
  3345.  
  3346. /* Execute the requested functions against the builtin commands */
  3347.  
  3348.     while ((carg = argv[OptionIndex++]) != NOWORD)
  3349.     {
  3350.     for (bp = builtin;
  3351.         (bp->command != (char *)NULL) && (NOCASE_COMPARE (bp->command, carg) != 0);
  3352.         bp++)
  3353.         continue;
  3354.  
  3355. /* Command is not builtin */
  3356.  
  3357.     if (bp->command == (char *)NULL)
  3358.     {
  3359.         printf (BasicErrorMessage, carg, NotBuiltinCommand);
  3360.         ReturnValue = 1;
  3361.         continue;
  3362.     }
  3363.  
  3364. /* Update on the action */
  3365.  
  3366.     switch (action)
  3367.     {
  3368.         case 0:
  3369.         printf (BasicErrorMessage, carg, (bp->mode & BLT_CURRENT)
  3370.                             ? LIT_builtin : "external");
  3371.         break;
  3372.  
  3373.         case 1:
  3374.         bp->mode |= BLT_CURRENT;
  3375.         break;
  3376.  
  3377.         case 2:
  3378.         if (bp->mode & BLT_ALWAYS)
  3379.             printf (BasicErrorMessage, carg, "always builtin");
  3380.  
  3381.         else
  3382.             bp->mode &= ~BLT_CURRENT;
  3383.  
  3384.         break;
  3385.     }
  3386.     }
  3387.  
  3388.     return ReturnValue;
  3389. }
  3390.  
  3391. /*
  3392.  * Report Usage error
  3393.  */
  3394.  
  3395. static int F_LOCAL    UsageError (char *string)
  3396. {
  3397.     return PrintWarningMessage ("Usage: %s", string) + 1;
  3398. }
  3399.  
  3400. /*
  3401.  * Alias command: alias [ -t] [ name [=commands] ]...
  3402.  */
  3403.  
  3404. static int doalias (int argc, char **argv)
  3405. {
  3406.     int            ReturnValue = 0;
  3407.     int            i = 1;
  3408.     bool        tracked = FALSE;
  3409.     char        *path = (char *)NULL;
  3410.     char        *cp;
  3411.  
  3412. /* Check for tracked aliases */
  3413.  
  3414.     if ((argc > 1) && (strcmp (argv[1], "-t") == 0))
  3415.     {
  3416.     ++i;
  3417.     tracked = TRUE;
  3418.     }
  3419.  
  3420. /* List only? */
  3421.  
  3422.     if (argc <= i)
  3423.     return PrintAllAlias (tracked);
  3424.  
  3425. /* Set them up or print them */
  3426.  
  3427.     while (i < argc)
  3428.     {
  3429.  
  3430. /* Not tracked - either set alias value if there is an equals or display
  3431.  * the alias value
  3432.  */
  3433.  
  3434.     if (!tracked)
  3435.     {
  3436.         if ((cp = strchr (argv[i], '=')) != (char *)NULL)
  3437.         *(cp++) = 0;
  3438.  
  3439. /* Check for valid name */
  3440.  
  3441.         if (!IsValidAliasName (argv[i], TRUE))
  3442.         return PrintWarningMessage (LIT_Emsg, LIT_alias, NotValidAlias,
  3443.                         argv[i]);
  3444.  
  3445. /* Save it if appropriate */
  3446.  
  3447.         if (cp != (char *)NULL)
  3448.         {
  3449.         if (!SaveAlias (argv[i], cp, tracked))
  3450.             ReturnValue = 1;
  3451.         }
  3452.  
  3453. /* Print it */
  3454.  
  3455.         else if (LookUpAlias (argv[i], FALSE) == (AliasList *)NULL)
  3456.         {
  3457.         PrintWarningMessage (NotAnAlias, LIT_alias, argv[i]);
  3458.         ReturnValue = 1;
  3459.         }
  3460.  
  3461.         else
  3462.         PrintAlias (argv[i]);
  3463.     }
  3464.  
  3465. /* Set up tracked alias */
  3466.  
  3467.     else if (!IsValidAliasName (argv[i], TRUE))
  3468.         return PrintWarningMessage (LIT_Emsg, LIT_alias, NotValidAlias,
  3469.                     argv[i]);
  3470.  
  3471.     else if ((path == (char *)NULL) &&
  3472.          ((path = AllocateMemoryCell (FFNAME_MAX + 4)) == (char *)NULL))
  3473.         return doOutofMemory (*argv);
  3474.  
  3475. /* Save the new path for the alias */
  3476.  
  3477.     else if (SaveAlias (argv[i],
  3478.                 (FindLocationOfExecutable (path, argv[i])
  3479.                 != EXTENSION_NOT_FOUND) ? path : null, tracked))
  3480.         ReturnValue = 1;
  3481.  
  3482.     ++i;
  3483.     }
  3484.  
  3485.     return ReturnValue;
  3486. }
  3487.  
  3488. /*
  3489.  * UnAlias command: unalias name...
  3490.  */
  3491.  
  3492. static int dounalias (int argc, char **argv)
  3493. {
  3494.     int            i;
  3495.  
  3496. /* Set them up or print them */
  3497.  
  3498.     for (i = 1; i < argc; ++i)
  3499.     {
  3500.     if (LookUpAlias (argv[i], FALSE) != (AliasList *)NULL)
  3501.         DeleteAlias (argv[i]);
  3502.  
  3503.     else
  3504.         PrintWarningMessage (NotAnAlias, LIT_unalias, argv[i]);
  3505.     }
  3506.  
  3507.     return 0;
  3508. }
  3509.  
  3510. /*
  3511.  * OS2 detach function
  3512.  */
  3513.  
  3514. #if (OS_TYPE != OS_DOS)
  3515. static int    dodetach (int argc, char **argv)
  3516. {
  3517.     int        RetVal;
  3518.  
  3519.     if (argc < 2)
  3520.     return UsageError ("detach program [ parameters ... ]");
  3521.  
  3522.     argv++;
  3523.     RetVal = ExecuteACommand (argv, EXEC_SPAWN_DETACH);
  3524.     argv--;
  3525.     return RetVal;
  3526. }
  3527. #endif
  3528.  
  3529. /*
  3530.  * Start a session
  3531.  */
  3532.  
  3533. #if (OS_TYPE == OS_OS2)
  3534. static int dostart (int argc, char **argv)
  3535. {
  3536.     bool        Direct = FALSE;
  3537.     bool        UseCMD = FALSE;
  3538.     STARTDATA        stdata;
  3539.     STARTDATA        *sdp = &stdata;
  3540.     Word_B        *wb = (Word_B *)NULL;
  3541.     Word_B        *ep = (Word_B *)NULL;
  3542.     int            c;
  3543.     bool        options = FALSE;
  3544.     bool        SetDefault = FALSE;
  3545.     char        ChangeEnv = 0;
  3546.     bool        FirstArg = TRUE;
  3547.     bool        Display = FALSE;
  3548.     OSCALL_RET        rc;
  3549.     OSCALL_PARAM    ulSessID;
  3550.     char        *sp;
  3551.     char        p_name[FFNAME_MAX];
  3552.     char        *SetPWD = GetVariableAsString (PWDVariable, FALSE);
  3553.  
  3554. /* Initialise the session control info */
  3555.  
  3556.     memset (&stdata, 0, sizeof (STARTDATA));
  3557.     stdata.Length = sizeof (STARTDATA);
  3558.     stdata.FgBg = SSF_FGBG_BACK;    /* Set background session    */
  3559.     stdata.PgmTitle = (PGM_TITLE_TYPE *)NULL;
  3560.     stdata.Related = SSF_RELATED_CHILD;
  3561.     stdata.TraceOpt = SSF_TRACEOPT_NONE;
  3562.     stdata.InheritOpt = SSF_INHERTOPT_SHELL;
  3563.     stdata.IconFile = (char *)NULL;
  3564.     stdata.PgmHandle = 0L;
  3565.     stdata.PgmControl = SSF_CONTROL_NOAUTOCLOSE;
  3566.     stdata.InitXPos = 0;
  3567.     stdata.InitYPos = 0;
  3568.     stdata.InitXSize = 100;
  3569.     stdata.InitYSize = 100;
  3570.  
  3571. /* These values get reset somewhere along the line */
  3572.  
  3573. #  if (OS_SIZE == OS_16)
  3574.     stdata.SessionType = SSF_TYPE_WINDOWABLEVIO;
  3575. #  else
  3576.     stdata.SessionType = SSF_TYPE_DEFAULT;
  3577. #  endif
  3578.  
  3579.     stdata.Environment = (PBYTE)1;    /* Build the environment */
  3580.  
  3581. /* Switch on the arguments */
  3582.  
  3583.     ResetGetOptions ();            /* Reset GetOptions        */
  3584.  
  3585.     while ((c = GetOptions (argc, argv,
  3586.                 SetDefault ? "t:hHfWPFibIDxe:c:"
  3587.                        : "O:St:dhHfWPFxibCIe:c:A:X:", 0)) != EOF)
  3588.     {
  3589.     switch (c)
  3590.     {
  3591.         case 'O':
  3592.         if (!FirstArg)
  3593.             return UsageError (StartUsage);
  3594.         
  3595.         else if (!stricmp (OptionArgument, LIT_dos))
  3596.             sdp = &DOS_SessionControlBlock;
  3597.  
  3598.         else if (!stricmp (OptionArgument, "pm"))
  3599.             sdp = &PM_SessionControlBlock;
  3600.         
  3601.         else
  3602.             return UsageError (StartUsage);
  3603.         
  3604.         SetDefault = TRUE;
  3605.         break;
  3606.  
  3607.         case 'A':
  3608.             if ((options) || (OptionIndex != argc))
  3609.             return UsageError (StartUsage);
  3610.  
  3611.          ulSessID = (OSCALL_PARAM) strtoul (OptionArgument, &sp, 0);
  3612.  
  3613.          if (*sp)
  3614.             return UsageError (StartUsage);
  3615.  
  3616. #  if (OS_SIZE == OS_16)
  3617.          if ((rc = DosSelectSession (ulSessID, 0L)))
  3618. #  else
  3619.          if ((rc = DosSelectSession (ulSessID)))
  3620. #  endif
  3621.             return PrintWarningMessage (Start_NoSession, ulSessID,
  3622.                             GetOSSystemErrorMessage (rc));
  3623.          return 0;
  3624.  
  3625.         case 'c':        /* Set program control        */
  3626.         {
  3627.         sdp->PgmControl = 0;
  3628.         sp = OptionArgument;
  3629.  
  3630.         while (*sp)
  3631.         {
  3632.             switch (*(sp++))
  3633.             {
  3634.             case 'v':
  3635.                 sdp->PgmControl |= SSF_CONTROL_VISIBLE;
  3636.                 sdp->PgmControl &= ~SSF_CONTROL_INVISIBLE;
  3637.                 break;
  3638.  
  3639.             case 'i':
  3640.                 sdp->PgmControl |= SSF_CONTROL_INVISIBLE;
  3641.                 sdp->PgmControl &= ~SSF_CONTROL_VISIBLE;
  3642.                 break;
  3643.  
  3644.             case 'l':
  3645.                 sdp->PgmControl |= SSF_CONTROL_MAXIMIZE;
  3646.                 sdp->PgmControl &= ~SSF_CONTROL_MINIMIZE;
  3647.                 break;
  3648.  
  3649.             case 's':
  3650.                 sdp->PgmControl |= SSF_CONTROL_MINIMIZE;
  3651.                 sdp->PgmControl &= ~SSF_CONTROL_MAXIMIZE;
  3652.                 break;
  3653.  
  3654.             case 'n':
  3655.                 sdp->PgmControl |= SSF_CONTROL_NOAUTOCLOSE;
  3656.                 break;
  3657.  
  3658.             case 'a':
  3659.                 sdp->PgmControl &= ~SSF_CONTROL_NOAUTOCLOSE;
  3660.                 break;
  3661.  
  3662.             default:
  3663.                 return PrintWarningMessage
  3664.                 ("start: option must be one of [vilsna]") + 1;
  3665.             }
  3666.         }
  3667.  
  3668.         break;
  3669.         }
  3670.  
  3671.         case 'X':            /* Set startup directory    */
  3672.         SetPWD = OptionArgument;
  3673.         break;
  3674.  
  3675.         case 't':            /* Set title            */
  3676.         if (SetDefault && (sdp->PgmTitle != (PGM_TITLE_TYPE *)NULL))
  3677.             ReleaseMemoryCell (sdp->PgmTitle);
  3678.  
  3679.         sdp->PgmTitle = (!strlen (OptionArgument))
  3680.                     ? (char *)NULL
  3681.                     : SetDefault
  3682.                     ? StringSave (OptionArgument)
  3683.                         : OptionArgument;
  3684.  
  3685.         if (strlen (OptionArgument) > 32)
  3686.             sdp->PgmTitle[33] = 0;
  3687.  
  3688.         break;
  3689.  
  3690.         case 'S':            /* Script            */
  3691.         Direct = FALSE;
  3692.         break;
  3693.  
  3694.         case 'd':            /* Direct             */
  3695.         Direct = TRUE;
  3696.         break;
  3697.  
  3698.         case 'H':            /* Inherit options        */
  3699.         sdp->InheritOpt = SSF_INHERTOPT_SHELL;
  3700.         break;
  3701.  
  3702.         case 'h':            /* Inherit options        */
  3703.         sdp->InheritOpt = SSF_INHERTOPT_PARENT;
  3704.         break;
  3705.  
  3706.         case 'f':            /* Foreground            */
  3707.         sdp->FgBg = SSF_FGBG_FORE;
  3708.         break;
  3709.  
  3710.         case 'b':            /* Background            */
  3711.         sdp->FgBg = SSF_FGBG_BACK;
  3712.         break;
  3713.  
  3714.             case 'I':
  3715.         sdp->Related = SSF_RELATED_INDEPENDENT;
  3716.         break;
  3717.  
  3718.             case 'x':
  3719.         sdp->Related = SSF_RELATED_CHILD;
  3720.         break;
  3721.  
  3722.         case 'W':            /* PM Window            */
  3723.         sdp->FgBg = SSF_FGBG_FORE;
  3724.             sdp->SessionType = (sdp == &DOS_SessionControlBlock)
  3725.                     ? SSF_TYPE_WINDOWABLEVIO
  3726.                     : SSF_TYPE_WINDOWEDVDM;
  3727.         break;
  3728.  
  3729.         case 'P':            /* PM Session            */
  3730.         sdp->FgBg = SSF_FGBG_FORE;
  3731.             sdp->SessionType = (sdp == &DOS_SessionControlBlock)
  3732.                     ? SSF_TYPE_PM
  3733.                     : SSF_TYPE_WINDOWEDVDM;
  3734.         break;
  3735.  
  3736.         case 'F':            /* Full Screen            */
  3737.         sdp->FgBg = SSF_FGBG_FORE;
  3738.             sdp->SessionType = (sdp == &DOS_SessionControlBlock)
  3739.                     ? SSF_TYPE_FULLSCREEN
  3740.                     : SSF_TYPE_VDM;
  3741.         break;
  3742.  
  3743.         case 'C':            /* Use the CMD processor    */
  3744.         UseCMD = TRUE;
  3745.         break;
  3746.  
  3747.         case 'D':            /* Show options            */
  3748.         Display = TRUE;
  3749.         break;
  3750.  
  3751.         case 'e':            /* Define environment        */
  3752.         if (ChangeEnv == 'i')
  3753.             return UsageError (StartUsage);
  3754.  
  3755.         if ((sdp->Environment != (PBYTE)NULL) &&
  3756.             (sdp->Environment != (PBYTE)1))
  3757.         {
  3758.             ReleaseMemoryCell (sdp->Environment);
  3759.             sdp->Environment = (PBYTE)NULL;
  3760.         }
  3761.  
  3762.         ChangeEnv = 'e';
  3763.         ep = AddWordToBlock (OptionArgument, ep);
  3764.         break;
  3765.  
  3766.         case 'i':            /* Inherit environment        */
  3767.         if (ChangeEnv == 'e')
  3768.             return UsageError (StartUsage);
  3769.  
  3770.         if ((sdp->Environment != (PBYTE)NULL) &&
  3771.             (sdp->Environment != (PBYTE)1))
  3772.             ReleaseMemoryCell (sdp->Environment);
  3773.  
  3774.         ChangeEnv = 'i';
  3775.         sdp->Environment = (PBYTE)NULL;
  3776.         break;
  3777.  
  3778.         default:
  3779.         return UsageError (StartUsage);
  3780.     }
  3781.  
  3782.     FirstArg = FALSE;
  3783.     options = TRUE;
  3784.     }
  3785.  
  3786. /* If setting default, no more parameters allowed */
  3787.  
  3788.     if (SetDefault)
  3789.     {
  3790.     if (OptionIndex != argc) 
  3791.         return UsageError (StartUsage);
  3792.     }
  3793.  
  3794.     else
  3795.     {
  3796.  
  3797. /* Check for script */
  3798.  
  3799.     if ((OptionIndex != argc) && (!UseCMD) && (!Direct) &&
  3800.         (FindLocationOfExecutable (p_name, argv[OptionIndex])
  3801.                      == EXTENSION_EXECUTABLE))
  3802.         Direct = TRUE;
  3803.  
  3804. /* Find the program to start */
  3805.  
  3806.     if ((OptionIndex == argc) || (!Direct))
  3807.     {
  3808.         if (!UseCMD)
  3809.         {
  3810.         wb = AddWordToBlock (GetVariableAsString (ShellVariableName,
  3811.                               FALSE), wb);
  3812.         if (SetPWD != null)
  3813.         {
  3814.             wb = AddWordToBlock ("-X", wb);
  3815.             wb = AddWordToBlock (SetPWD, wb);
  3816.         }
  3817.         }
  3818.  
  3819.         else
  3820.         {
  3821.         wb = AddWordToBlock (GetVariableAsString (ComspecVariable,
  3822.                               FALSE), wb);
  3823.         if (OptionIndex != argc)
  3824.             wb = AddWordToBlock ("/c", wb);
  3825.         }
  3826.     }
  3827.  
  3828.     else
  3829.         wb = AddWordToBlock (argv[OptionIndex++], wb);
  3830.     }
  3831.  
  3832. /* Set up environment */
  3833.  
  3834.     if ((ep != (Word_B *)NULL) &&
  3835.     ((sdp->Environment =
  3836.         BuildOS2String (GetWordList (AddWordToBlock (NOWORD, ep)), 0))
  3837.                 == (PBYTE)NULL))
  3838.     return doOutofMemory ("start");
  3839.  
  3840. /* OK, if we set the default, processing is completed */
  3841.  
  3842.     if (SetDefault)
  3843.     {
  3844.     if (ep != (Word_B *)NULL)
  3845.         SetMemoryAreaNumber (sdp->Environment, 0);
  3846.  
  3847.     if (Display)
  3848.        DisplayStartData (sdp);
  3849.  
  3850.     return 0;
  3851.     }
  3852.  
  3853. /* Set up the session block and execute the command */
  3854.  
  3855.     SessionControlBlock = &stdata;
  3856.  
  3857. /* Build the argument block */
  3858.  
  3859.     while (OptionIndex != argc)
  3860.     wb = AddWordToBlock (argv[OptionIndex++], wb);
  3861.  
  3862. /* Start the session */
  3863.  
  3864.     argv = GetWordList (AddWordToBlock (NOWORD, wb));
  3865.  
  3866.     return (ExecuteACommand (argv, 0) == -1) ? 1 : 0;
  3867. }
  3868.  
  3869. /*
  3870.  * Clean up the start data structure, removing allocated space
  3871.  */
  3872.  
  3873. static void F_LOCAL DisplayStartData (STARTDATA *sdp)
  3874. {
  3875.     char    *sp;
  3876.  
  3877.     printf ("Start session defaults for %s mode\n",
  3878.         (sdp == &DOS_SessionControlBlock) ? LIT_dos : "PM");
  3879.  
  3880.     printf ("    Window mode: %s%s%s %sautoclose.\n",
  3881.         (sdp->PgmControl & SSF_CONTROL_INVISIBLE)
  3882.         ? "invisible" : "visible",
  3883.         (sdp->PgmControl & SSF_CONTROL_MINIMIZE)
  3884.         ? " minimised" : null,
  3885.         (sdp->PgmControl & SSF_CONTROL_MAXIMIZE)
  3886.         ? " maximised" : null,
  3887.         (sdp->PgmControl & SSF_CONTROL_NOAUTOCLOSE)
  3888.         ? "no " : "");
  3889.  
  3890.     if (sdp->PgmTitle != (PGM_TITLE_TYPE *)NULL)
  3891.     printf ("    Program Title: %s\n", sdp->PgmTitle);
  3892.  
  3893.     printf ("    Run in %s.\n", (sdp->FgBg == SSF_FGBG_FORE)
  3894.                     ? "foreground" : "background");
  3895.  
  3896.     printf ("    %s session.\n",
  3897.         (sdp->Related == SSF_RELATED_INDEPENDENT)
  3898.         ? "Independent" : "Dependent");
  3899.  
  3900.     printf ("    Session type: %s.\n", 
  3901.         (sdp->SessionType == SSF_TYPE_WINDOWABLEVIO)
  3902.         ? "Windowed"
  3903.         : (sdp->SessionType == SSF_TYPE_PM)
  3904.         ? "PM"
  3905.         : (sdp->SessionType == SSF_TYPE_FULLSCREEN)
  3906.             ? "Full screen"
  3907.             : (sdp->SessionType == SSF_TYPE_WINDOWEDVDM)
  3908.             ? "DOS Windowed"
  3909.             : (sdp->SessionType == SSF_TYPE_VDM)
  3910.                 ? "DOS Full screen"
  3911.                 : "Default");
  3912.  
  3913.     printf ("    Inherit %s environment\n", 
  3914.         (sdp->InheritOpt == SSF_INHERTOPT_SHELL) ? "start up" : "current");
  3915.  
  3916.     if (sdp->Environment == (PBYTE)1)
  3917.     puts ("    Use current environment variables.");
  3918.  
  3919.     else if (sdp->Environment == (PBYTE)NULL)
  3920.     puts ("    Use start up environment variables.");
  3921.  
  3922.     else
  3923.     {
  3924.     sp = sdp->Environment;
  3925.     printf ("    Environment variables:\n");
  3926.  
  3927.     while (*sp)
  3928.     {
  3929.         printf ("        %s\n", sp);
  3930.         sp += strlen (sp) + 1;
  3931.     }
  3932.     }
  3933. }
  3934. #endif
  3935.  
  3936.  
  3937. /*
  3938.  * Set up new Parameter Variables
  3939.  */
  3940.  
  3941. static int F_LOCAL SetUpNewParameterVariables (char **Array,/* New values*/
  3942.                            int  Offset, /* Start offset */
  3943.                            int  Max, /* Number    */
  3944.                            char *function)
  3945. {
  3946.     int        n;
  3947.     Word_B    *wb = (Word_B *)NULL;
  3948.     char    *cp;
  3949.     bool    Duplicate = (bool)(strcmp (function, LIT_shift) == 0);
  3950.  
  3951.     if ((wb = AddParameter (ParameterArray[0], wb, function)) == (Word_B *)NULL)
  3952.     return 1;
  3953.  
  3954.     for (n = Offset; n < Max; ++n)
  3955.     {
  3956.     if ((cp = (Duplicate) ? StringSave (Array[n]) : Array[n])
  3957.         == null)
  3958.         return doOutofMemory (function);
  3959.  
  3960.     if ((wb = AddParameter (cp, wb, function)) == (Word_B *)NULL)
  3961.         return 1;
  3962.     }
  3963.  
  3964.     return (AddParameter (NOWORD, wb, function) == (Word_B *)NULL)
  3965.         ? 1 : 0;
  3966. }
  3967.  
  3968. /*
  3969.  * dolet - Each arg is an arithmetic expression to be evaluated.
  3970.  */
  3971.  
  3972. static int dolet (int argc, char **argv)
  3973. {
  3974.     long    Value = 0;
  3975.     int        i;
  3976.  
  3977. /* If (( )), ignore the terminating )) */
  3978.  
  3979.     if ((strcmp (argv[0], "((") == 0) &&
  3980.     (strcmp (argv[argc - 1], "))") == 0))
  3981.     argv[argc - 1] = NOWORD;
  3982.  
  3983.     ExpansionErrorDetected = FALSE;
  3984.  
  3985.     for (i = 1; !ExpansionErrorDetected && (argv[i] != NOWORD); ++i)
  3986.     Value = EvaluateMathsExpression (argv[i]);
  3987.  
  3988.     return !Value || ExpansionErrorDetected ? 1 : 0;
  3989. }
  3990.  
  3991. /*
  3992.  * Out of memory error
  3993.  */
  3994.  
  3995. static int F_LOCAL doOutofMemory (char *s)
  3996. {
  3997.     return PrintWarningMessage (BasicErrorMessage, s, Outofmemory1);
  3998. }
  3999.  
  4000. /*
  4001.  * MSDOS, EXPORT and READONLY functions: xxxx [ variable names... ]
  4002.  */
  4003.  
  4004. static int doexport (int argc, char **argv)
  4005. {
  4006.     return UpdateVariableStatus (argv + 1, STATUS_EXPORT);
  4007. }
  4008.  
  4009. static int doreadonly (int argc, char **argv)
  4010. {
  4011.     return UpdateVariableStatus (argv + 1, STATUS_READONLY);
  4012. }
  4013.  
  4014. static int domsdos (int argc, char **argv)
  4015. {
  4016.     return UpdateVariableStatus (argv + 1, STATUS_CONVERT_MSDOS);
  4017. }
  4018.  
  4019. static int F_LOCAL UpdateVariableStatus (char **argv, unsigned int Mask)
  4020. {
  4021.     if (*argv == NOWORD)
  4022.         return ListAllVariables (Mask, FALSE);
  4023.  
  4024.     else
  4025.     {
  4026.     memset (&TypesetValues, 0, sizeof (TypesetValues));
  4027.     TypesetValues.Flags_On = Mask;
  4028.     return TypesetVariables (argv);
  4029.     }
  4030. }
  4031.  
  4032. /*
  4033.  * List All variables matching a STATUS
  4034.  */
  4035.  
  4036. static int F_LOCAL ListAllVariables (unsigned int Mask, bool PrintValue)
  4037. {
  4038.     HandleSECONDandRANDOM ();
  4039.  
  4040.     DVE_Mask = Mask;
  4041.     DVE_PrintValue = PrintValue;
  4042.  
  4043.     twalk (VariableTree, DisplayVariableEntry);
  4044.     return 0;
  4045. }
  4046.  
  4047. /*
  4048.  * TWALK Function - display VARIABLE tree
  4049.  */
  4050.  
  4051. static void DisplayVariableEntry (const void *key, VISIT visit, int level)
  4052. {
  4053.     VariableList    *vp = *(VariableList **)key;
  4054.  
  4055.     if ((visit == postorder) || (visit == leaf))
  4056.     {
  4057.     if ((IS_VariableFC ((int)*vp->name)) &&
  4058.         ((vp->status & DVE_Mask) ||
  4059.              (((vp->status & ~STATUS_GLOBAL) == 0) && (DVE_Mask == 0xffff))))
  4060.         PrintEntry (vp, DVE_PrintValue,
  4061.             (DVE_Mask == 0xffff) ? 0 : DVE_Mask);
  4062.     }
  4063. }
  4064.  
  4065. /*
  4066.  * typeset function - [ [ [+-][Hflprtux] ] [+-][LRZi[n]] [ name [=value] ...]
  4067.  */
  4068.  
  4069. static int dotypeset (int argc, char **argv)
  4070. {
  4071.     int            ReturnValue = 0;
  4072.     bool        f_flag = FALSE;
  4073.     unsigned int    *CurrentFlags;
  4074.     int            tmp = 0;
  4075.     char        c_opt;
  4076.     char        *cp;
  4077.  
  4078. /* Initialise save area */
  4079.  
  4080.     memset (&TypesetValues, 0, sizeof (TypesetValues));
  4081.     OptionIndex = 1;
  4082.  
  4083. /* Scan the options */
  4084.  
  4085.     while ((cp = argv[OptionIndex]) != NOWORD)
  4086.     {
  4087.     if ((*cp != '-') && (*cp != '+'))
  4088.         break;
  4089.  
  4090.     CurrentFlags = (*cp == '-') ? &TypesetValues.Flags_On
  4091.                     : &TypesetValues.Flags_Off;
  4092.  
  4093.     while (*(++cp))
  4094.     {
  4095.         switch (*cp)
  4096.         {
  4097.         case 'p':
  4098.             fprintf (stderr, "typeset: Option (%c) not supported\n",
  4099.                  *cp);
  4100.             break;
  4101.  
  4102.         case 'H':
  4103.             *CurrentFlags |= STATUS_CONVERT_MSDOS;
  4104.             break;
  4105.  
  4106.         case 'f':        /* Function only        */
  4107.             f_flag = TRUE;
  4108.             break;
  4109.  
  4110.         case 'l':
  4111.             *CurrentFlags |= STATUS_LOWER_CASE;
  4112.             break;
  4113.  
  4114.         case 'r':
  4115.             *CurrentFlags |= STATUS_READONLY;
  4116.             break;
  4117.  
  4118.         case 't':
  4119.             *CurrentFlags |= STATUS_TAGGED;
  4120.             break;
  4121.  
  4122.         case 'u':
  4123.             *CurrentFlags |= STATUS_UPPER_CASE;
  4124.             break;
  4125.  
  4126.         case 'x':
  4127.             *CurrentFlags |= STATUS_EXPORT;
  4128.             break;
  4129.  
  4130.         case 'L':
  4131.         case 'R':
  4132.         case 'Z':
  4133.         case 'i':
  4134.         {
  4135.             switch (c_opt = *cp)
  4136.             {
  4137.             case 'L':
  4138.                 *CurrentFlags |= STATUS_LEFT_JUSTIFY;
  4139.                 break;
  4140.  
  4141.             case 'R':
  4142.                 *CurrentFlags |= STATUS_RIGHT_JUSTIFY;
  4143.                 break;
  4144.  
  4145.             case 'Z':
  4146.                 *CurrentFlags |= STATUS_ZERO_FILL;
  4147.                 break;
  4148.  
  4149.             case 'i':
  4150.                 *CurrentFlags |= STATUS_INTEGER;
  4151.                 break;
  4152.             }
  4153.  
  4154. /* Only set width on on */
  4155.  
  4156.             if (CurrentFlags != &TypesetValues.Flags_On)
  4157.             break;
  4158.  
  4159. /* Check for following numeric */
  4160.  
  4161.             if (isdigit (*(cp + 1)))
  4162.             tmp = (int)strtol (cp + 1, &cp, 10);
  4163.  
  4164.             else if ((*(cp + 1) == 0) &&
  4165.                  (OptionIndex + 1 < argc) &&
  4166.                  isdigit (*argv[OptionIndex + 1]))
  4167.             tmp = (int)strtol (argv[++OptionIndex], &cp, 10);
  4168.  
  4169.             else
  4170.             break;
  4171.  
  4172. /* Check for invalid numeric */
  4173.  
  4174.             if (!tmp || *(cp--))
  4175.             return UsageError (TypeSetUsage);
  4176.  
  4177. /* Width or base */
  4178.  
  4179.             if (c_opt == 'i')
  4180.             TypesetValues.Base = tmp;
  4181.  
  4182.             else
  4183.             TypesetValues.Width = tmp;
  4184.  
  4185.             break;
  4186.         }
  4187.  
  4188.         default:
  4189.             return UsageError (TypeSetUsage);
  4190.         }
  4191.     }
  4192.  
  4193.     ++OptionIndex;
  4194.     }
  4195.  
  4196. /* Check for f flag - function processing. */
  4197.  
  4198.     if (f_flag)
  4199.     {
  4200.     if (((TypesetValues.Flags_On | TypesetValues.Flags_Off) &
  4201.         ~(STATUS_TAGGED | STATUS_EXPORT)) ||
  4202.         (!(TypesetValues.Flags_On | TypesetValues.Flags_Off)))
  4203.         return PrintWarningMessage ("typeset: Only -xt allowed with -f");
  4204.  
  4205.     for (tmp = OptionIndex; tmp < argc; tmp++)
  4206.         ReturnValue |= HandleFunction (argv[tmp]);
  4207.  
  4208.     return ReturnValue;
  4209.     }
  4210.  
  4211. /* Process variable assignments */
  4212.  
  4213.     return TypesetVariables (&argv[OptionIndex]);
  4214. }
  4215.  
  4216. static int F_LOCAL TypesetVariables (char **argv)
  4217. {
  4218.     bool        PrintValue = C2bool (TypesetValues.Flags_Off);
  4219.     VariableList    *vp;
  4220.     char        *CurrentName;
  4221.     char        *NewValue;
  4222.     char        *OldValue;
  4223.     int            OldStatus;
  4224.     long        NValue;
  4225.     char        *xp;
  4226.     int            Retval = 0;
  4227.     unsigned int    Mask;
  4228.     long        Index;
  4229.  
  4230.     if ((Mask = (TypesetValues.Flags_On | TypesetValues.Flags_Off)) == 0)
  4231.     Mask = 0xffff;
  4232.  
  4233. /* Switch off any appropriate flags */
  4234.  
  4235.     if (TypesetValues.Flags_On & STATUS_LOWER_CASE)
  4236.     TypesetValues.Flags_Off |= STATUS_UPPER_CASE;
  4237.  
  4238.     if (TypesetValues.Flags_On & STATUS_UPPER_CASE)
  4239.     TypesetValues.Flags_Off |= STATUS_LOWER_CASE;
  4240.  
  4241.     if (TypesetValues.Flags_On & STATUS_RIGHT_JUSTIFY)
  4242.     TypesetValues.Flags_Off |= STATUS_LEFT_JUSTIFY;
  4243.  
  4244.     if (TypesetValues.Flags_On & STATUS_LEFT_JUSTIFY)
  4245.     TypesetValues.Flags_Off |= STATUS_RIGHT_JUSTIFY;
  4246.  
  4247. /* If no arguments, print all values matching the mask */
  4248.  
  4249.     if (*argv == NOWORD)
  4250.     return ListAllVariables (Mask, PrintValue);
  4251.  
  4252. /* Process each argument.  If no flags, print it */
  4253.  
  4254.     while ((CurrentName = *(argv++)) != NOWORD)
  4255.     {
  4256.     if (!GetVariableName (CurrentName, &Index, &NewValue, (bool *)NULL))
  4257.     {
  4258.         PrintErrorMessage (BasicErrorMessage, CurrentName, LIT_BadID);
  4259.         return 1;
  4260.     }
  4261.  
  4262. /* Convert the = to a null so we get the name and value */
  4263.  
  4264.     if (*NewValue)
  4265.         *(NewValue++) = 0;
  4266.  
  4267.     else
  4268.         NewValue = (char *)NULL;
  4269.  
  4270. /* If valid - update, otherwise print a message */
  4271.  
  4272.     if ((Mask != 0xffff) || (NewValue != (char *)NULL))
  4273.     {
  4274.  
  4275. /* Get the original value */
  4276.  
  4277.         vp = LookUpVariable (CurrentName, (int)Index, TRUE);
  4278.         OldStatus = vp->status;
  4279.         OldValue = GetVariableArrayAsString (CurrentName, (int)Index,
  4280.                          FALSE);
  4281.  
  4282. /* Update status */
  4283.  
  4284.         vp->status &= ~(TypesetValues.Flags_Off);
  4285.         vp->status |= TypesetValues.Flags_On;
  4286.  
  4287.         if ((CurrentFunction != (FunctionList *)NULL) &&
  4288.         (!(vp->status & STATUS_GLOBAL)))
  4289.         vp->status |= STATUS_LOCAL;
  4290.  
  4291.         if (Index)
  4292.         vp->status &= ~(STATUS_EXPORT);
  4293.  
  4294. /* Set up a new integer value.  If the variable was not an integer
  4295.  * originally and there is an error, unset it
  4296.  */
  4297.  
  4298.         xp = (NewValue != (char *)NULL) ? NewValue : OldValue;
  4299.  
  4300.         if (vp->status & STATUS_INTEGER)
  4301.         {
  4302.         if (ValidMathsExpression (xp, &NValue))
  4303.         {
  4304.             Retval = PrintWarningMessage (LIT_Emsg, "bad numeric value",
  4305.                           CurrentName, xp);
  4306.  
  4307.             if (!(OldStatus & STATUS_INTEGER))
  4308.             UnSetVariable (CurrentName, (int)Index, FALSE);
  4309.  
  4310.             continue;
  4311.         }
  4312.  
  4313.         else if (OldStatus & STATUS_READONLY)
  4314.         {
  4315.             Retval = PrintWarningMessage (LIT_2Strings, vp->name,
  4316.                           LIT_IsReadonly);
  4317.             continue;
  4318.         }
  4319.  
  4320. /* Save the new integer value and set up base etc */
  4321.  
  4322.         vp->nvalue = NValue;
  4323.  
  4324.         if (!vp->base)
  4325.             vp->base = (LastNumberBase != -1) ? LastNumberBase : 10;
  4326.  
  4327.         if (TypesetValues.Base)
  4328.             vp->base = TypesetValues.Base;
  4329.  
  4330.         if (vp->value != null)
  4331.             ReleaseMemoryCell ((void *)vp->value);
  4332.  
  4333.         vp->value = null;
  4334.         }
  4335.  
  4336. /* String - update if appropriate, both the value and the width */
  4337.  
  4338.         else if ((OldStatus & STATUS_READONLY) ||
  4339.              (!(vp->status & STATUS_READONLY)))
  4340.         SetVariableArrayFromString (CurrentName, (int)Index, xp);
  4341.  
  4342. /* New status is readonly - allow set, then stop them */
  4343.  
  4344.         else
  4345.         {
  4346.         vp->status &= ~STATUS_READONLY;
  4347.         SetVariableArrayFromString (CurrentName, (int)Index, xp);
  4348.         vp->status |= STATUS_READONLY;
  4349.         }
  4350.  
  4351.         if (TypesetValues.Width)
  4352.         vp->width = TypesetValues.Width;
  4353.     }
  4354.  
  4355. /* Print if appropriate */
  4356.  
  4357.     else
  4358.         PrintEntry (LookUpVariable (CurrentName, (int)Index, FALSE),
  4359.             PrintValue, Mask);
  4360.     }
  4361.  
  4362.     return Retval;
  4363. }
  4364.  
  4365. static void F_LOCAL PrintEntry (VariableList    *vp,
  4366.                     bool        PrintValue,
  4367.                     unsigned int    Mask)
  4368. {
  4369.     unsigned int    Flags = vp->status & Mask;
  4370.  
  4371.     if (vp->status & STATUS_NOEXISTANT)
  4372.     return;
  4373.  
  4374.     if (Flags & STATUS_INTEGER)
  4375.     printf ("integer ");
  4376.  
  4377.     if (Flags & STATUS_LEFT_JUSTIFY)
  4378.     printf ("left justified %d ", vp->width);
  4379.  
  4380.     if (Flags & STATUS_RIGHT_JUSTIFY)
  4381.     printf ("right justified %d ", vp->width);
  4382.  
  4383.     if (Flags & STATUS_ZERO_FILL)
  4384.     printf ("zero filled %d ", vp->width);
  4385.  
  4386.     if (Flags & STATUS_CONVERT_MSDOS)
  4387.     printf ("MS-DOS Format ");
  4388.  
  4389.     if (Flags & STATUS_LOWER_CASE)
  4390.     printf ("lowercase ");
  4391.  
  4392.     if (Flags & STATUS_UPPER_CASE)
  4393.     printf ("uppercase ");
  4394.  
  4395.     if (Flags & STATUS_READONLY)
  4396.     printf ("readonly ");
  4397.  
  4398.     if (Flags & STATUS_TAGGED)
  4399.     printf ("tagged ");
  4400.  
  4401.     if (Flags & STATUS_EXPORT)
  4402.     printf ("exported ");
  4403.  
  4404. /* Print the value */
  4405.  
  4406.     foputs (vp->name);
  4407.  
  4408.     if (vp->index || CountVariableArraySize (vp->name) > 1)
  4409.     printf (LIT_BNumber, vp->index);
  4410.  
  4411.     if (PrintValue)
  4412.     printf ("=%s", GetVariableArrayAsString (vp->name, vp->index, TRUE));
  4413.  
  4414.     fputchar (CHAR_NEW_LINE);
  4415. }
  4416.  
  4417. /*
  4418.  * Handle typeset -f
  4419.  */
  4420.  
  4421. static int F_LOCAL HandleFunction (char *name)
  4422. {
  4423.     FunctionList    *fop;
  4424.  
  4425.     if (strchr (name, CHAR_ASSIGN) != (char *)NULL)
  4426.     return PrintWarningMessage ("typeset: cannot assign to functions");
  4427.  
  4428.     if ((fop = LookUpFunction (name, FALSE)) == (FunctionList *)NULL)
  4429.     return PrintWarningMessage ("typeset: function %s does not exist",
  4430.                     name);
  4431.  
  4432.     fop->Traced = C2bool (TypesetValues.Flags_On & STATUS_TAGGED);
  4433.     return 0;
  4434. }
  4435.  
  4436. /*
  4437.  * Modified version of getopt for shell
  4438.  */
  4439.  
  4440. void    ResetGetOptions (void)
  4441. {
  4442.     OptionIndex = 1;            /* Reset the optind flag    */
  4443.     GetOptionPosition = 1;        /* Current position    */
  4444. }
  4445.  
  4446. int    GetOptions (int        argc,        /* Argument count    */
  4447.             char    **argv,        /* Argument array    */
  4448.             char    *optstring,    /* Options string    */
  4449.             int        flags)        /* Control flags    */
  4450. {
  4451.     int        cur_option;        /* Current option        */
  4452.     char    *cp;            /* Character pointer        */
  4453.  
  4454.     BadOptionValue = 0;
  4455.  
  4456.     if (GetOptionPosition == 1)
  4457.     {
  4458.  
  4459. /* Special for doecho */
  4460.  
  4461.     if (flags & GETOPT_PRINT)
  4462.         return EOF;
  4463.  
  4464. /* Check for out of range, correct start character and not single */
  4465.  
  4466.     if ((OptionIndex >= argc) ||
  4467.         (!(((OptionStart = *argv[OptionIndex]) == '-') ||
  4468.            (((flags & GETOPT_PLUS) && (OptionStart == '+'))))) ||
  4469.         (!argv[OptionIndex][1]))
  4470.         return EOF;
  4471.  
  4472.     if (!strcmp (argv[OptionIndex], DoubleHypen))
  4473.         return EOF;
  4474.     }
  4475.  
  4476. /* Get the current character from the current argument vector */
  4477.  
  4478.     cur_option = argv[OptionIndex][GetOptionPosition];
  4479.  
  4480. /* Validate it */
  4481.  
  4482.     if ((cur_option == ':') ||
  4483.     ((cp = strchr (optstring, cur_option)) == (char *)NULL))
  4484.     {
  4485.     if (flags & GETOPT_MESSAGE)
  4486.         PrintWarningMessage ("%s: illegal option -- %c", argv[0],
  4487.                  cur_option);
  4488.  
  4489. /* Save the bad option value and move to the next offset */
  4490.  
  4491.     BadOptionValue = cur_option;
  4492.  
  4493.     if (!argv[OptionIndex][++GetOptionPosition])
  4494.     {
  4495.         OptionIndex++;
  4496.         GetOptionPosition = 1;
  4497.     }
  4498.  
  4499.     return '?';
  4500.     }
  4501.  
  4502. /* Parameters following ? */
  4503.  
  4504.     OptionArgument = (char *)NULL;
  4505.  
  4506.     if (*(++cp) == ':')
  4507.     {
  4508.     if (argv[OptionIndex][GetOptionPosition + 1])
  4509.         OptionArgument = &argv[OptionIndex++][GetOptionPosition + 1];
  4510.  
  4511.     else if (++OptionIndex >= argc)
  4512.     {
  4513.         if ((flags & (GETOPT_MESSAGE | GETOPT_AMISSING)) == GETOPT_MESSAGE)
  4514.         PrintWarningMessage ("%s: option (%c) requires an argument",
  4515.                      argv[0], cur_option);
  4516.  
  4517.         BadOptionValue = cur_option;
  4518.         OptionArgument = (char *)-1;
  4519.         GetOptionPosition = 1;
  4520.         return '?';
  4521.     }
  4522.  
  4523.     else
  4524.         OptionArgument = argv[OptionIndex++];
  4525.  
  4526.     GetOptionPosition = 1;
  4527.     }
  4528.  
  4529.     else if (!argv[OptionIndex][++GetOptionPosition])
  4530.     {
  4531.     GetOptionPosition = 1;
  4532.     OptionIndex++;
  4533.     }
  4534.  
  4535.     return cur_option;
  4536. }
  4537.  
  4538.  
  4539. /*
  4540.  * Kill the specified processes
  4541.  */
  4542.  
  4543. #if (OS_TYPE != OS_DOS)
  4544. static struct KillSignalList {
  4545.     char    *Name;
  4546.     int        SigVal;
  4547. } KillSignalList [] = {
  4548.     {"term",    -1 },
  4549. #  if (OS_TYPE == OS_OS2)
  4550.     {"usr1",    PFLG_A },
  4551.     {"usr2",    PFLG_B },
  4552.     {"usr3",    PFLG_C },
  4553. #  elif (OS_TYPE == OS_NT)
  4554.     {"break",    CTRL_BREAK_EVENT},
  4555.     {"int",    CTRL_C_EVENT},
  4556. #  endif
  4557. };
  4558.  
  4559. #define MAX_KILL_SIGNALS    ARRAY_SIZE (KillSignalList)
  4560.  
  4561. static int dokill (int argc, char **argv)
  4562. {
  4563.     int        i;
  4564.     int        Sigv = -1;
  4565.     char    *cp;
  4566.     PID        pidProcess;
  4567.     long    value;
  4568. #  if (OS_TYPE == OS_NT)
  4569.     HANDLE    hp;
  4570. #  elif (OS_TYPE == OS_OS2)
  4571.     USHORT    rc;
  4572. #  endif
  4573.  
  4574.     if (argc < 2)
  4575.     return UsageError (KillUsage);
  4576.  
  4577. /* List signals ? */
  4578.  
  4579.     if (!strcmp (argv[1], "-l"))
  4580.     {
  4581.     for (i = 0; i < MAX_KILL_SIGNALS; ++i)
  4582.         puts (KillSignalList[i].Name);
  4583.  
  4584.     return 0;
  4585.     }
  4586.  
  4587. /* Look up signal name */
  4588.  
  4589.     if (**(++argv) == '-')
  4590.     {
  4591.     cp = &argv[0][1];
  4592.  
  4593.     for (i = 0; i < MAX_KILL_SIGNALS; ++i)
  4594.     {
  4595.         if (!stricmp (KillSignalList[i].Name, cp))
  4596.         break;
  4597.     }
  4598.  
  4599.     if (i == MAX_KILL_SIGNALS)
  4600.         return PrintWarningMessage ("kill: bad signal name (%s)", cp);
  4601.  
  4602.     Sigv = KillSignalList[i].SigVal;
  4603.  
  4604.     if (*(++argv) == NOWORD)
  4605.         return UsageError (KillUsage);
  4606.     }
  4607.  
  4608. /* Kill the processes */
  4609.  
  4610.     while (*argv != NOWORD)
  4611.     {
  4612.  
  4613. /* Find the PID */
  4614.  
  4615.         if (((**argv == CHAR_JOBID) && !ConvertJobToPid ((*argv) + 1, &pidProcess)) ||
  4616.         ((**argv != CHAR_JOBID) && !ConvertNumericValue (*argv, &value, 0)))
  4617.         return PrintWarningMessage ("kill: bad process/job id (%s)",
  4618.                      *argv);
  4619.  
  4620. /* If Process ID, its in value */
  4621.  
  4622.      if (**argv != CHAR_JOBID)
  4623.         pidProcess = (PID)value;
  4624.  
  4625. /* Send the signal */
  4626.  
  4627. #  if (OS_TYPE == OS_OS2)
  4628.     if (Sigv == -1)
  4629.     {
  4630. #    if (OS_SIZE == OS_16)
  4631.         rc = DosKillProcess (DKP_PROCESSTREE, pidProcess);
  4632. #    else
  4633.         if ((rc = DosKillProcess (DKP_PROCESSTREE, pidProcess)))
  4634.          rc = (USHORT)DosSendSignalException (pidProcess,
  4635.                               XCPT_SIGNAL_BREAK);
  4636. #    endif
  4637.     }
  4638.  
  4639.     else
  4640.         rc = Dos32FlagProcess (pidProcess, FLGP_SUBTREE, Sigv, 0);
  4641.  
  4642.     if (rc)
  4643.         return PrintWarningMessage ("kill: Cannot signal process %s\n%s",
  4644.                      *argv,
  4645.                      GetOSSystemErrorMessage (rc));
  4646. #  elif (OS_TYPE == OS_NT)
  4647.  
  4648. /* Check for Ctl C or Break */
  4649.  
  4650.     if ((Sigv == CTRL_BREAK_EVENT) || (Sigv == CTRL_C_EVENT))
  4651.     {
  4652.         if (!GenerateConsoleCtrlEvent (Sigv, pidProcess))
  4653.         return PrintWarningMessage ("kill: Cannot kill process %s\n%s",
  4654.                         *argv, 
  4655.                      GetOSSystemErrorMessage (GetLastError ()));
  4656.  
  4657.     }
  4658.  
  4659. /* Open the process and terminate it */
  4660.  
  4661.     else if ((hp = OpenProcess (PROCESS_ALL_ACCESS, TRUE,
  4662.                     pidProcess)) == NULL)
  4663.         return PrintWarningMessage ("kill: Cannot access process %s\n%s",
  4664.                     *argv, 
  4665.                     GetOSSystemErrorMessage (GetLastError ()));
  4666.     else if (!TerminateProcess (hp, 1))
  4667.     {
  4668.         PrintWarningMessage ("kill: Cannot kill process %s\n%s", *argv, 
  4669.                  GetOSSystemErrorMessage (GetLastError ()));
  4670.         CloseHandle (hp);
  4671.         return 1;
  4672.     }
  4673.  
  4674.     CloseHandle (hp);
  4675. #  endif
  4676.  
  4677.     argv++;
  4678.     }
  4679.  
  4680.     return 0;
  4681. }
  4682.  
  4683. /*
  4684.  * Wait for process to end
  4685.  */
  4686.  
  4687. static int dowait (int argc, char **argv)
  4688. {
  4689.     PID        pidProcess;
  4690.     int        TermStat;
  4691. #  if (OS_TYPE != OS_NT)
  4692.     int        ReturnValue;
  4693. #  endif
  4694.     long    value;
  4695.  
  4696. /* Check usage */
  4697.  
  4698.     if (argc > 2)
  4699.     return UsageError ("wait [ job ]");
  4700.  
  4701. /* Wait for all jobs ? */
  4702.  
  4703.     if (argc < 2)
  4704.     {
  4705. #  if (OS_TYPE == OS_NT)
  4706.     return PrintWarningMessage ("wait: any job not supported on NT");
  4707. #  else
  4708.     TermStat = -1;
  4709.  
  4710. /* Yes - wait until wait returns an error */
  4711.  
  4712.     while ((pidProcess = wait (&ReturnValue)) != -1)
  4713.     {
  4714.         DeleteJob (pidProcess);
  4715.         TermStat = ReturnValue;
  4716.     }
  4717. #  endif
  4718.     }
  4719.  
  4720. /* Wait for a specific process.  Job or PID? */
  4721.  
  4722.     else
  4723.     {
  4724.  
  4725. /* Move to the ID */
  4726.  
  4727.     argv++;
  4728.  
  4729. /* Find the PID */
  4730.  
  4731.         if (((**argv == CHAR_JOBID) &&
  4732.          !ConvertJobToPid ((*argv) + 1, &pidProcess)) ||
  4733.         ((**argv != CHAR_JOBID) &&
  4734.          !ConvertNumericValue (*argv, &value, 0)))
  4735.         return PrintWarningMessage ("wait: bad process/job id (%s)",
  4736.                      *argv);
  4737.  
  4738. /* If Process ID, its in value */
  4739.  
  4740.      if (**argv != CHAR_JOBID)
  4741.         pidProcess = (PID)value;
  4742.  
  4743. /* Wait for the specified process */
  4744.  
  4745. #  if (OS_TYPE == OS_UNIX)
  4746.     fputs ("UNIX: Wait for process NI\n", stderr);
  4747.  
  4748. #  else
  4749.     if (cwait (&TermStat, pidProcess, WAIT_GRANDCHILD) == -1)
  4750.         return PrintWarningMessage ("wait: Process id (%s) not active",
  4751.                     *argv);
  4752. #  endif
  4753.  
  4754.     DeleteJob (pidProcess);
  4755.     }
  4756.  
  4757. /* Convert termination status to return code */
  4758.  
  4759.     if (TermStat == -1)
  4760.     return -1;
  4761.  
  4762.     else if (TermStat & 0x00ff)
  4763.     return TermStat & 0x00ff;
  4764.  
  4765.     else
  4766.     return (TermStat >> 8) & 0x00ff;
  4767. }
  4768.  
  4769. /*
  4770.  * Print the job info
  4771.  */
  4772.  
  4773. static int dojobs (int argc, char **argv)
  4774. {
  4775.     bool    ListMode = FALSE;
  4776.     bool    ListTree = FALSE;
  4777.     pid_t    p = getpid ();
  4778. #  if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_UNIX)
  4779.     long    value;
  4780. #  endif
  4781.     int        i;
  4782.  
  4783. /* List signals ? */
  4784.  
  4785.     ResetGetOptions ();            /* Reset GetOptions        */
  4786.  
  4787.     while ((i = GetOptions (argc, argv, "plP:", 0)) != EOF)
  4788.     {
  4789.         switch (i)
  4790.         {
  4791.         case 'l':
  4792.         ListMode = TRUE;
  4793.         break;
  4794.  
  4795. #  if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_UNIX)
  4796.         case 'p':
  4797.         ListTree = TRUE;
  4798.         break;
  4799.  
  4800.         case 'P':
  4801.         ListTree = TRUE;
  4802.  
  4803.         if (!ConvertNumericValue (OptionArgument, &value, 0))
  4804.             return PrintWarningMessage ("jobs: bad process id (%s)",
  4805.                             OptionArgument);
  4806.  
  4807.         p = (pid_t)value;
  4808.  
  4809.         break;
  4810. #  endif
  4811.  
  4812.         default:
  4813.         return UsageError (JobUsage);
  4814.     }
  4815.     }
  4816.  
  4817. #  if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_UNIX)
  4818.     if (ListTree)
  4819.         return PrintProcessTree (p);
  4820. #  endif
  4821.  
  4822. /* Look up job name */
  4823.  
  4824.     return PrintJobs (ListMode);
  4825. }
  4826. #endif
  4827.  
  4828. /*
  4829.  * Print the option settings
  4830.  */
  4831.  
  4832. static int F_LOCAL    PrintOptionSettings (void)
  4833. {
  4834.     int        i;
  4835.  
  4836.     puts ("Current option settings:");
  4837.  
  4838.     for (i = 0; SetOptions[i].OptionName != (char *)NULL; i++)
  4839.     printf ("%-16s%s\n", SetOptions[i].OptionName,
  4840.         TestOptionValue (SetOptions[i].OptionName, FALSE)
  4841.             ? "on" : "off");
  4842.     return 0;
  4843. }
  4844.  
  4845. /*
  4846.  * Change option value
  4847.  */
  4848.  
  4849. static bool F_LOCAL ChangeOptionValue (char *value, bool set)
  4850. {
  4851.     struct SetOptions    *entry = LookUpOptionValue (value);
  4852.  
  4853.     if (entry == (struct SetOptions *)NULL)
  4854.     return FALSE;
  4855.  
  4856.     else if (entry->HasOptionValue)
  4857.     SetClearFlag (entry->FlagValue, set);
  4858.  
  4859. /* If one of the Editor flags, disable all editor flags first */
  4860.  
  4861.     else if (entry->FlagValue == FLAGS_VERIFY_SWITCH)
  4862.     SetVerifyStatus (set);
  4863.  
  4864. #ifdef FLAGS_BREAK_SWITCH
  4865.     else if (entry->FlagValue == FLAGS_BREAK_SWITCH)
  4866.     SetBreakStatus (set);
  4867. #endif
  4868.  
  4869. #ifdef FLAGS_SET_OS2
  4870.     else if (entry->FlagValue == FLAGS_SET_OS2)
  4871.         BaseOS = (set) ? BASE_OS_OS2 : BASE_OS_DOS;
  4872. #endif
  4873.  
  4874. #ifdef FLAGS_SET_NT
  4875.     else if (entry->FlagValue == FLAGS_SET_NT)
  4876.         BaseOS = (set) ? BASE_OS_NT : BASE_OS_DOS;
  4877. #endif
  4878.  
  4879. /* Change the editor flags */
  4880.  
  4881.     else if (set)
  4882.     {
  4883.     if (entry->FlagValue & FLAGS_EDITORS)
  4884.         ShellGlobalFlags &= ~(FLAGS_EDITORS);
  4885.  
  4886.     ShellGlobalFlags |= entry->FlagValue;
  4887.     }
  4888.  
  4889.     else
  4890.     ShellGlobalFlags &= ~(entry->FlagValue);
  4891.  
  4892.  
  4893.     return TRUE;
  4894. }
  4895.  
  4896. /*
  4897.  * Update shell switches
  4898.  */
  4899.  
  4900. static void F_LOCAL SetClearFlag (int Flag, bool set)
  4901. {
  4902.     if (set)
  4903.     FL_SET (Flag);
  4904.  
  4905.     else
  4906.     FL_CLEAR (Flag);
  4907. }
  4908.  
  4909. /*
  4910.  * Test an option
  4911.  */
  4912.  
  4913. static int F_LOCAL TestOptionValue (char *value, bool AllowJump)
  4914. {
  4915.     struct SetOptions    *entry = LookUpOptionValue (value);
  4916.  
  4917.     if (entry == (struct SetOptions *)NULL)
  4918.     {
  4919.     PrintWarningMessage ("%s: unknown option - %s", TestProgram, value);
  4920.  
  4921.     if (AllowJump)
  4922.         longjmp (TestErrorReturn, 1);
  4923.     
  4924.     return 0;
  4925.     }
  4926.  
  4927.     else if (entry->FlagValue == FLAGS_VERIFY_SWITCH)
  4928.     {
  4929. #if (OS_TYPE == OS_OS2)
  4930.     BOOL    fVerifyOn;
  4931.  
  4932.     DosQVerify (&fVerifyOn);
  4933.     return fVerifyOn;
  4934.  
  4935. #elif (OS_TYPE != OS_DOS)
  4936.  
  4937.     return FALSE;
  4938.  
  4939. #elif (OS_TYPE == OS_DOS)
  4940.  
  4941.     union REGS    r;
  4942.  
  4943.     r.x.REG_AX = 0x5400;
  4944.     DosInterrupt (&r, &r);
  4945.     return r.h.al;
  4946. #endif
  4947.     }
  4948.  
  4949. #if (OS_TYPE == OS_DOS)
  4950.     else if (entry->FlagValue == FLAGS_BREAK_SWITCH)
  4951.     {
  4952.     union REGS    r;
  4953.  
  4954.     r.x.REG_AX = 0x3300;
  4955.     DosInterrupt (&r, &r);
  4956.     return r.h.dl;
  4957.     }
  4958. #endif
  4959.  
  4960. #ifdef FLAGS_SET_OS2
  4961.     else if (entry->FlagValue == FLAGS_SET_OS2)
  4962.         return BaseOS == BASE_OS_OS2;
  4963. #endif
  4964.  
  4965. #ifdef FLAGS_SET_NT
  4966.     else if (entry->FlagValue == FLAGS_SET_NT)
  4967.         return BaseOS == BASE_OS_NT;
  4968. #endif
  4969.  
  4970.     else if (entry->HasOptionValue)
  4971.     return (FL_TEST (entry->FlagValue) != 0);
  4972.  
  4973.     return (ShellGlobalFlags & entry->FlagValue);
  4974. }
  4975.  
  4976. /*
  4977.  * Find an Option entry
  4978.  */
  4979.  
  4980. static struct SetOptions * F_LOCAL LookUpOptionValue (char *value)
  4981. {
  4982.     int        i = 0;
  4983.     char    *cp;
  4984.  
  4985.     while ((cp = SetOptions[i].OptionName) != (char *)NULL)
  4986.     {
  4987.     if (!strcmp (cp, value))
  4988.         return &SetOptions[i];
  4989.  
  4990.     ++i;
  4991.     }
  4992.  
  4993.     return (struct SetOptions *)NULL;
  4994. }
  4995.  
  4996. /*
  4997.  * Get Unit number
  4998.  */
  4999.  
  5000. static int F_LOCAL GetUnitNumber (char *prog)
  5001. {
  5002.     int        Unit;
  5003.  
  5004.     if (((Unit = GetNumericValue (OptionArgument)) < 0) || (Unit > 9))
  5005.     {
  5006.     PrintWarningMessage (LIT_Emsg, LIT_bun, prog, OptionArgument);
  5007.     return -1;
  5008.     }
  5009.  
  5010.     return Unit;
  5011. }
  5012.  
  5013. /*
  5014.  * fc function - fc [-e EditorName] [-nlr] [First [Last]]
  5015.  *         fc -e - [Old=New] [Command]
  5016.  */
  5017.  
  5018. static int dofc (int argc, char **argv)
  5019. {
  5020.     char        *Editor = GetVariableAsString (FCEditVariable, FALSE);
  5021.     bool        n_flag = TRUE;
  5022.     bool        l_flag = FALSE;
  5023.     bool        r_flag = FALSE;
  5024.     int            EventNumber[2];
  5025.     char        *Temp;
  5026.     char        *Change = (char *)NULL;
  5027.     char        *Change1;
  5028.     char        *NewBuffer;
  5029.     char        *NewArg[3];
  5030.     int            i;
  5031.     FILE        *fp;
  5032.  
  5033. /* Check status */
  5034.  
  5035.     if (!(InteractiveFlag && IS_TTY (0)))
  5036.     return PrintWarningMessage ("fc: only available in interactive mode");
  5037.  
  5038.     if ((NewBuffer = AllocateMemoryCell (LINE_MAX + 3)) == (char *)NULL)
  5039.     return doOutofMemory ("fc");
  5040.  
  5041. /* Process options */
  5042.  
  5043.     ResetGetOptions ();            /* Reset GetOptions        */
  5044.  
  5045.     while ((i = GetOptions (argc, argv, "e:nlr", 0)) != EOF)
  5046.     {
  5047.     switch (i)
  5048.     {
  5049.         case 'e':            /* editor name            */
  5050.         Editor = OptionArgument;
  5051.         break;
  5052.  
  5053.         case 'n':    n_flag = FALSE;    break;
  5054.         case 'l':    l_flag = TRUE;    break;
  5055.         case 'r':    r_flag = TRUE;    break;
  5056.  
  5057.         default:
  5058.         return UsageError ("fc [ -e EditorName ] [ -nlr ] [ First [Last]]\n       fc -e - [ Old=New ] [ Command ]");
  5059.     }
  5060.     }
  5061.  
  5062.     argv += OptionIndex;
  5063.     argc -= OptionIndex;
  5064.  
  5065. /* Check for [old=new] */
  5066.  
  5067.     if (argc && ((Change1 = strchr (*argv, CHAR_ASSIGN)) != (char *)NULL))
  5068.     {
  5069.     Change = *(argv++);
  5070.     *(Change1++) = 0;
  5071.     --argc;
  5072.     }
  5073.  
  5074.     if (!l_flag)
  5075.     DeleteLastHistory ();
  5076.  
  5077. /* Get the first and last event number */
  5078.  
  5079.     for (i = 0; i < 2; i++)
  5080.     {
  5081.     EventNumber[i] = 0;
  5082.  
  5083.     if (argc)
  5084.     {
  5085.         EventNumber[i] = (int)strtol (*argv, &Temp, 10);
  5086.  
  5087.         if (*Temp)
  5088.         EventNumber[i] = SearchHistory (*argv);
  5089.  
  5090.         else if (EventNumber[i] < 0)
  5091.         EventNumber[i] += GetLastHistoryEvent () - 1;
  5092.  
  5093.         if (EventNumber[i] <= 0)
  5094.         return PrintWarningMessage ("fc: event <%s> not found",
  5095.                         *argv);
  5096.  
  5097.         argv++;
  5098.         --argc;
  5099.     }
  5100.     }
  5101.  
  5102. /* Set up first and last values */
  5103.  
  5104.     i = GetLastHistoryEvent () - 1;
  5105.  
  5106.     if (!EventNumber[0])
  5107.     {
  5108.         if ((EventNumber[0] = (l_flag) ? i - 16 : i) <= 0)
  5109.         EventNumber[0] = 1;
  5110.     }
  5111.  
  5112.     if (!EventNumber[1])
  5113.         EventNumber[1] = (l_flag) ? i : EventNumber[0];
  5114.  
  5115. /* If l - print */
  5116.  
  5117.     if (l_flag)
  5118.     fp = stdout;
  5119.  
  5120.     else if (Editor == null)
  5121.     return PrintWarningMessage ("fc: editor not defined");
  5122.  
  5123.     else if ((fp = FOpenFile ((Temp = GenerateTemporaryFileName ()),
  5124.                       sOpenWriteBinaryMode)) == (FILE *)NULL)
  5125.     return PrintWarningMessage ("fc: cannot create %s", Temp);
  5126.  
  5127. /* Process history */
  5128.  
  5129.     if (!l_flag)
  5130.     n_flag = FALSE;
  5131.  
  5132.     PrintHistory (r_flag, n_flag, EventNumber[0], EventNumber[1], fp);
  5133.  
  5134.     if (l_flag)
  5135.     return 0;
  5136.  
  5137. /* Check that we found some history */
  5138.  
  5139.     if (!ftell (fp))
  5140.     l_flag = TRUE;
  5141.  
  5142.     if (fp != stdout)
  5143.     CloseFile (fp);
  5144.  
  5145.     if (l_flag)
  5146.     {
  5147.     unlink (Temp);
  5148.     return PrintWarningMessage ("fc: no matches");
  5149.     }
  5150.  
  5151. /* Invoke the editor */
  5152.  
  5153.     if (strcmp (Editor, ShellOptionsVariable))
  5154.     {
  5155.     NewArg[0] = Editor;
  5156.     NewArg[1] = Temp;
  5157.     NewArg[2] = (char *)NULL;
  5158.  
  5159.     if (ExecuteACommand (NewArg, 0) == -1)
  5160.     {
  5161.         unlink (Temp);
  5162.         return 1;
  5163.     }
  5164.     }
  5165.  
  5166. /* Now execute it */
  5167.  
  5168.     if ((i = S_open (TRUE, Temp, O_RMASK)) < 0)
  5169.     return PrintWarningMessage ("fc: cannot re-open edit file (%s)",
  5170.                     Temp);
  5171.  
  5172.     argc = read (i, NewBuffer, LINE_MAX + 1);
  5173.     S_close (i, TRUE);
  5174.  
  5175.     if (argc <= 0)
  5176.     return (argc == 0) ? 0 : 1;
  5177.  
  5178.     else if (argc >= LINE_MAX - 1)
  5179.     return PrintWarningMessage (FCTooLong);
  5180.  
  5181. /* Strip off trailing EOFs and EOLs */
  5182.  
  5183.     CleanUpBuffer (argc, NewBuffer, 0x1a);
  5184.  
  5185. /* Check for substitution */
  5186.  
  5187.     if (Change == (char *)NULL)
  5188.     strcpy (ConsoleLineBuffer, NewBuffer);
  5189.  
  5190.     else
  5191.     {
  5192.     if ((Temp = strstr (NewBuffer, Change)) == (char *)NULL)
  5193.         return PrintWarningMessage ("fc: string not found");
  5194.  
  5195.     if ((i = strlen (NewBuffer) - strlen (Change) +
  5196.          strlen (Change1)) >= LINE_MAX - 2)
  5197.         return PrintWarningMessage (FCTooLong);
  5198.  
  5199. /* Do the substitution */
  5200.  
  5201.     i = Temp - NewBuffer;
  5202.     strncpy (ConsoleLineBuffer, NewBuffer, i);
  5203.     strcpy (ConsoleLineBuffer + i, Change1);
  5204.     strcat (ConsoleLineBuffer, NewBuffer + strlen (Change) + i);
  5205.     }
  5206.  
  5207.     ReleaseMemoryCell ((void *)NewBuffer);
  5208.  
  5209. /* Tell the user what we've done */
  5210.  
  5211.     puts (ConsoleLineBuffer);
  5212.  
  5213. /* Flag the console driver not to read from the console, but use the
  5214.  * current contents of the ConsoleLineBuffer
  5215.  */
  5216.  
  5217.     UseConsoleBuffer = TRUE;
  5218.     return 0;
  5219. }
  5220.  
  5221. /*
  5222.  * Convert Job ID to process id
  5223.  */
  5224.  
  5225.  
  5226. #if (OS_TYPE != OS_DOS) 
  5227. static bool F_LOCAL ConvertJobToPid (char *String, PID *pid)
  5228. {
  5229.     long    value;
  5230.     JobList    *jp;
  5231.  
  5232. /* If numeric value, look up the job number */
  5233.  
  5234.     if (ConvertNumericValue (String, &value, 0))
  5235.     jp = LookUpJob ((int)value);
  5236.  
  5237.     else
  5238.     jp = SearchForJob (String);
  5239.  
  5240.     if (jp == (JobList *)NULL)
  5241.     return FALSE;
  5242.  
  5243.     PreviousJob = CurrentJob;
  5244.     CurrentJob = jp->pid;
  5245.  
  5246.     *pid = jp->pid;
  5247.     return TRUE;
  5248. }
  5249. #endif
  5250.  
  5251. /*
  5252.  * Missing OS2 2.x API
  5253.  */
  5254.  
  5255. #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32)
  5256. APIRET     DosQFileMode (PSZ pszFName, PULONG pusAttr)
  5257. {
  5258.     APIRET    rc;
  5259.     FILESTATUS3    status;
  5260.  
  5261.     if ((rc = DosQueryPathInfo (pszFName, FIL_STANDARD, &status,
  5262.                    sizeof (FILESTATUS3))) == 0)
  5263.     *pusAttr = status.attrFile;
  5264.  
  5265.     return rc;
  5266. }
  5267. #endif
  5268.  
  5269. #if (OS_TYPE == OS_NT)
  5270. int     DosQFileMode (char *pszFName, DWORD *pusAttr)
  5271. {
  5272.     *pusAttr = GetFileAttributes (pszFName);
  5273.     return (*pusAttr == 0xffffffff) ? -1 : 0;
  5274. }
  5275. #endif
  5276.  
  5277. /*
  5278.  * A sort of version of times
  5279.  */
  5280.  
  5281. #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32) && !defined (__WATCOMC__)
  5282. static int dotimes (int argc, char **argv)
  5283. {
  5284.     return PrintTimes ();
  5285. }
  5286. #endif
  5287.  
  5288. #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_UNIX)
  5289. static int dotimes (int argc, char **argv)
  5290. {
  5291.     return PrintTimes ();
  5292. }
  5293. #endif
  5294.  
  5295. /*
  5296.  * Convert logical path to physical path, skipping out SUBST drives
  5297.  */
  5298.  
  5299. #if (OS_TYPE == OS_OS2)
  5300. static char * F_LOCAL GetPhysicalPath (char *inpath, bool AllowNetwork)
  5301. {
  5302. #  ifndef __EMX__
  5303.     char        *op;
  5304.     char        *res;
  5305.  
  5306.     if ((op = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
  5307.     return inpath;
  5308.  
  5309.     if ((res = _fullpath (op, inpath, PATH_MAX + 6)) != (char *)NULL)
  5310.     strcpy (inpath, op);
  5311.  
  5312.     PATH_TO_LOWER_CASE (inpath);
  5313.     ReleaseMemoryCell (op);
  5314. #  endif
  5315.  
  5316.     return PATH_TO_UNIX (inpath);
  5317. }
  5318. #endif
  5319.  
  5320. /* NT Version */
  5321.  
  5322. #if (OS_TYPE == OS_NT)
  5323. static char * F_LOCAL GetPhysicalPath (char *inpath, bool AllowNetwork)
  5324. {
  5325.     char        *op;
  5326.     char        *res;
  5327.  
  5328.     if ((op = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
  5329.     return inpath;
  5330.  
  5331.     if (!GetFullPathName (inpath, FFNAME_MAX, op, &res))
  5332.     strcpy (inpath, op);
  5333.  
  5334.     PATH_TO_LOWER_CASE (inpath);
  5335.     ReleaseMemoryCell (op);
  5336.     return PATH_TO_UNIX (inpath);
  5337. }
  5338. #endif
  5339.  
  5340. /* DOS version */
  5341.  
  5342. #if (OS_TYPE == OS_DOS)
  5343. static char * F_LOCAL GetPhysicalPath (char *inpath, bool AllowNetwork)
  5344. {
  5345. #  ifndef __EMX__
  5346.     char        *op;
  5347.     union REGS        r;
  5348.     struct SREGS    sr;
  5349.  
  5350.     if ((op = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
  5351.     return inpath;
  5352.  
  5353.     PATH_TO_DOS (inpath);
  5354.  
  5355.     r.h.ah     = 0x60;
  5356.     r.x.REG_SI = FP_OFF (inpath);
  5357.     sr.ds      = FP_SEG (inpath);
  5358.  
  5359.     r.x.REG_DI = FP_OFF (op);
  5360.     sr.es      = FP_SEG (op);
  5361.  
  5362.     DosExtendedInterrupt (&r, &r, &sr);
  5363.  
  5364. /* If we are succesfully and this is not a networked drive, replace the
  5365.  * original path
  5366.  */
  5367.  
  5368.     if ((!(r.x.REG_CFLAGS & 1)) &&
  5369.         (AllowNetwork || (strncmp (op, "\\\\", 2) != 0)))
  5370.     {
  5371.     strlwr (strcpy (inpath, op));
  5372.     ReleaseMemoryCell (op);
  5373.     }
  5374. #  endif
  5375.  
  5376.     return PATH_TO_UNIX (inpath);
  5377. }
  5378. #endif
  5379.  
  5380. /* UNIX Version */
  5381.  
  5382. #if (OS_TYPE == OS_UNIX)
  5383. static char * F_LOCAL GetPhysicalPath (char *inpath, bool AllowNetwork)
  5384. {
  5385. #  ifdef S_IFLNK
  5386.     char        *op;
  5387.     struct stat        s;
  5388.     char        *res;
  5389.     int            len;
  5390.  
  5391.     if (((op = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL) ||
  5392.     (lstat (inpath, &s) != 0) || (s.st_mode & S_IFMT) != S_IFLNK)
  5393.     return inpath;
  5394.  
  5395.     if ((len = readlink (inpath, op, FFNAME_MAX)) == -1)
  5396.     return inpath;
  5397.     
  5398.     op [len] = 0;
  5399.     strcpy (inpath, op);
  5400.     ReleaseMemoryCell (op);
  5401.  
  5402. #  endif
  5403.  
  5404.     return inpath;
  5405. }
  5406. #endif
  5407.  
  5408.  
  5409. /*
  5410.  * Change the verify status.  No NT or UNIX functionality.
  5411.  */
  5412.  
  5413. static void F_LOCAL SetVerifyStatus (bool On)
  5414. {
  5415. #if (OS_TYPE == OS_OS2) 
  5416.     DosSetVerify (On);
  5417. #elif (OS_TYPE == OS_DOS)
  5418.     union REGS    r;
  5419.  
  5420.     r.x.REG_AX = On ? 0x2e01 : 0x2e00;
  5421.     r.x.REG_DX = 0;
  5422.     DosInterrupt (&r, &r);
  5423. #endif
  5424. }
  5425.  
  5426. /*
  5427.  * Change the break status
  5428.  */
  5429.  
  5430. #if (OS_TYPE == OS_DOS)
  5431. static void F_LOCAL SetBreakStatus (bool On)
  5432. {
  5433.     union REGS    r;
  5434.  
  5435.     r.x.REG_AX = 0x3301;
  5436.     r.h.dl = (unsigned char)(On ? 1 : 0);
  5437.     DosInterrupt (&r, &r);
  5438. }
  5439. #endif
  5440.  
  5441. /*
  5442.  * Bind an EMACS keystroke to a editing command
  5443.  */
  5444.  
  5445. #if defined (FLAGS_EMACS) || defined (FLAGS_GMACS)
  5446. static int    dobind (int argc, char **argv)
  5447. {
  5448.     bool    macro = FALSE;
  5449.     char    *cp;
  5450.     int        c;
  5451.  
  5452.     ResetGetOptions ();            /* Reset GetOptions        */
  5453.  
  5454.     while ((c = GetOptions (argc, argv, "m", 0)) != EOF)
  5455.     {
  5456.     if (c == 'm')
  5457.         macro = TRUE;
  5458.     
  5459.     else
  5460.         return UsageError ("bind [ -m ] [ keystroke=edit command ] ...");
  5461.     }
  5462.  
  5463.     argv += OptionIndex;
  5464.  
  5465. /* List All ? */
  5466.  
  5467.     if (*argv == (char *)NULL)    /* list all */
  5468.     return BindKeyStroke ((char *)NULL, (char *)NULL, FALSE);
  5469.  
  5470. /* Otherwise, bind */
  5471.  
  5472.     c = 0;
  5473.  
  5474.     while (*argv != (char *)NULL)
  5475.     {
  5476.     if ((cp = strchr (*argv, '=')) != (char *)NULL)
  5477.         *(cp++) = 0;
  5478.  
  5479.     c |= BindKeyStroke (*(argv++), cp, macro);
  5480.     }
  5481.  
  5482.     return c;
  5483. }
  5484. #endif
  5485.  
  5486. static int    doshellinfo (int argc, char **argv)
  5487. {
  5488.     static char        *OSName [] = { LIT_dos, "Windows", "OS/2", "Win NT",
  5489.                            "UNIX"};
  5490.  
  5491.     printf ("Global Flags              = 0x%.4x\n", ShellGlobalFlags);
  5492.     printf ("Max # of File descriptors = %d\n", MaxNumberofFDs);
  5493.     printf ("Disabled Variables        = 0x%.4x\n", DisabledVariables);
  5494.     printf ("Execute stack depth       = %d\n", Execute_stack_depth);
  5495.     printf ("Memory stack depth        = %d\n", MemoryAreaLevel);
  5496.     printf ("IO stack depth            = %d\n", NSave_IO_E);
  5497.     printf ("Subshell stack depth      = %d\n", NSubShells);
  5498.     printf ("Underlying OS             = <%s>\n", OSName[BaseOS]);
  5499.     return 0;
  5500. }
  5501.  
  5502. /*
  5503.  * Flag OS/2 process for _EMX_
  5504.  */
  5505.  
  5506. #if defined (__EMX__) && (OS_TYPE == OS_OS2)
  5507. USHORT    Dos32FlagProcess (PID pid, USHORT fScope, USHORT usFlagNum,
  5508.               USHORT usFlagArg)
  5509. {
  5510.     return ((USHORT)
  5511.         (_THUNK_PROLOG (2 + 2 + 2 + 2);
  5512.          _THUNK_SHORT (pid);
  5513.          _THUNK_SHORT (fScope);
  5514.          _THUNK_SHORT (usFlagNum);
  5515.          _THUNK_SHORT (usFlagArg);
  5516.          _THUNK_CALL (Dos16FlagProcess)));
  5517. }
  5518. #endif
  5519.  
  5520. /*
  5521.  * Check file access
  5522.  */
  5523.  
  5524. static int F_LOCAL CheckFAccess (char *Name, int mode)
  5525. {
  5526.     int        fdn = CheckForFD (CheckDOSFileName (Name));
  5527. #if (OS_TYPE == OS_UNIX)
  5528.     struct stat        s;
  5529.     mode_t        tm = 0;
  5530. #else
  5531.     OSCALL_PARAM    usAttr;
  5532. #endif
  5533.  
  5534.     if (fdn < 0)
  5535.     return S_access (Name, mode);
  5536.  
  5537. #if (OS_TYPE == OS_UNIX)
  5538.     if (fstat (fdn, &s) < 0)
  5539.         return 0;
  5540.  
  5541.     if (mode == 0)
  5542.     return 1;
  5543.  
  5544. /* Find which bits to check */
  5545.  
  5546.     if (mode & W_OK)
  5547.     tm |= S_IWUSR;
  5548.  
  5549.     if (mode & R_OK)
  5550.     tm |= S_IRUSR;
  5551.  
  5552.     if (mode & X_OK)
  5553.     tm |= S_IXUSR;
  5554.  
  5555. /* Are we the owner, or group owner? */
  5556.  
  5557.     if (getuid () != st.st_uid)
  5558.     {
  5559.     tm >>= 3;
  5560.  
  5561.     if (getgid () != st.st_gid)
  5562.         tm >>= 3;
  5563.     }
  5564.  
  5565.     return s.st_mode & tm;
  5566. #else
  5567.  
  5568. /* Non-Unix is almost as much pain */
  5569.  
  5570.     if (OS_GetFHAttributes (fdn, &usAttr) != 0)
  5571.         return 0;
  5572.  
  5573.     if ((mode & W_OK) && (usAttr & OS_FILE_READONLY))
  5574.         return 0;
  5575.  
  5576.     if (mode & X_OK)
  5577.         return 0;
  5578.  
  5579.     return 1;
  5580. #endif
  5581. }
  5582.  
  5583. /*
  5584.  * Check file type
  5585.  */
  5586.  
  5587. static int F_LOCAL CheckFType (char *Name, mode_t mode)
  5588. {
  5589.     struct stat        s;
  5590.     int            fdn = CheckForFD (CheckDOSFileName (Name));
  5591. #if (OS_TYPE != OS_UNIX)
  5592.     int            ftype;
  5593. #endif
  5594.  
  5595. /*
  5596.  * File Name
  5597.  */
  5598.  
  5599.     if (fdn < 0)
  5600.         return (S_stat (Name, &s) && ((s.st_mode & S_IFMT) == mode)) ? 1 : 0;
  5601.  
  5602. /*
  5603.  * File descriptor
  5604.  */
  5605. #if (OS_TYPE == OS_UNIX)
  5606.     return ((fstat (fdn, &s) >= 0) && ((s.st_mode & S_IFMT) == mode)) ? 1 : 0;
  5607. #else
  5608.  
  5609. /* Usual OS/2, MSDOS, WINNT pain */
  5610.     ftype = GetDescriptorType (fdn);
  5611.  
  5612.     if ((mode == S_IFREG) && (ftype == DESCRIPTOR_FILE))
  5613.         return 1;
  5614.  
  5615.     if ((mode == S_IFCHR) && 
  5616.         ((ftype == DESCRIPTOR_DEVICE) || (ftype == DESCRIPTOR_CONSOLE)))
  5617.         return 1;
  5618.  
  5619.     return 0;
  5620. #endif
  5621. }
  5622.  
  5623. /*
  5624.  * Check file mode
  5625.  */
  5626.  
  5627. static int F_LOCAL CheckFMode (char *Name, mode_t mode)
  5628. {
  5629.     int            fdn = CheckForFD (CheckDOSFileName (Name));
  5630. #if (OS_TYPE == OS_UNIX)
  5631.     struct stat        s;
  5632. #else
  5633.     OSCALL_PARAM    usAttr;
  5634. #endif
  5635.  
  5636. #if (OS_TYPE == OS_UNIX)
  5637.     if (((fdn < 0) && !S_stat (Name, &s)) ||
  5638.     ((fdn >= 0) && (fstat (fdn, &s) < 0)))
  5639.     return 0;
  5640.  
  5641.     return (s.st_mode & mode);
  5642. #else
  5643.     if (fdn < 0)
  5644.     return (OS_GetFileAttributes (Name, &usAttr) == 0) && (usAttr & mode);
  5645.  
  5646.     return (OS_GetFHAttributes (fdn, &usAttr) == 0) && (usAttr & mode);
  5647. #endif
  5648. }
  5649.  
  5650. /*
  5651.  * Check file size
  5652.  */
  5653.  
  5654. static int F_LOCAL CheckFSize (char *Name)
  5655. {
  5656.     struct stat        s;
  5657.     int            fdn = CheckForFD (CheckDOSFileName (Name));
  5658.  
  5659.     if (((fdn < 0) && !S_stat (Name, &s)) ||
  5660. #if (OS_TYPE == OS_UNIX)
  5661.     ((fdn >= 0) && (fstat (fdn, &s) < 0)))
  5662. #else
  5663.     ((fdn >= 0) && ((s.st_size = filelength (fdn)) < 0)))
  5664. #endif
  5665.     return 0;
  5666.       
  5667.     return (s.st_size > 0L);
  5668. }
  5669.  
  5670. /*
  5671.  * Does this refer to a file descriptor - check for /dev/fd/n
  5672.  */
  5673.  
  5674. static int F_LOCAL CheckForFD (char *name)
  5675. {
  5676.     if (strncmp (name, LIT_devfd, sizeof (LIT_devfd) - 1) != 0)
  5677.     return -1;
  5678.     
  5679.     return atoi (name + sizeof (LIT_devfd) - 1);
  5680. }
  5681.  
  5682. /*
  5683.  * Get File Handler Attributes
  5684.  */
  5685.  
  5686. /*
  5687.  * Under DOS, there is no way to get the file attributes
  5688.  */
  5689.  
  5690. #if (OS_TYPE == OS_DOS)
  5691. static OSCALL_RET F_LOCAL OS_GetFHAttributes (int fd, OSCALL_PARAM *usAttr)
  5692. {
  5693.     if (lseek (fd, 0L, SEEK_CUR) == -1L)
  5694.         return -1;
  5695.  
  5696.     *usAttr = 0;
  5697.     return 0;
  5698. }
  5699. #endif
  5700.  
  5701. /*
  5702.  * OS/2 Version
  5703.  */
  5704.  
  5705. #if (OS_TYPE == OS_OS2)
  5706. static OSCALL_RET F_LOCAL OS_GetFHAttributes (int fd, OSCALL_PARAM *usAttr)
  5707. {
  5708.     OSCALL_RET        rc;
  5709. #  if (OS_SIZE == OS_32)
  5710.     FILESTATUS3        info;
  5711. #  else
  5712.     FILESTATUS        info;
  5713. #  endif
  5714.  
  5715.     rc = DosQFileInfo (fd, FIL_STANDARD, &info, sizeof (info));
  5716.  
  5717.     if (rc != 0)
  5718.     return rc;
  5719.  
  5720.     *usAttr = info.attrFile;
  5721.     return 0;
  5722. }
  5723. #endif
  5724.  
  5725. /*
  5726.  * NT Version
  5727.  */
  5728.  
  5729. #if (OS_TYPE == OS_NT)
  5730. static OSCALL_RET F_LOCAL OS_GetFHAttributes (int fd, OSCALL_PARAM *usAttr)
  5731. {
  5732.     extern long _CRTAPI1    _get_osfhandle (int);
  5733.     BY_HANDLE_FILE_INFORMATION    info;
  5734.     HANDLE            osfp = (HANDLE)_get_osfhandle (fp);
  5735.  
  5736.     switch (GetFileType (osfp))
  5737.  
  5738.     if (!GetFileInformationByHandle(osfp, &info))
  5739.     return 1;
  5740.  
  5741.     *usAttr = info.dwFileAttributes;
  5742.     return 0;
  5743. }
  5744. #endif
  5745.